<?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:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Rosco Kalis]]></title><description><![CDATA[Entrepreneurship • Software Engineering • Cryptocurrency]]></description><link>https://kalis.me/</link><image><url>https://kalis.me/favicon.png</url><title>Rosco Kalis</title><link>https://kalis.me/</link></image><generator>Ghost 2.21</generator><lastBuildDate>Mon, 06 Apr 2026 22:34:15 GMT</lastBuildDate><atom:link href="https://kalis.me/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[2024 Year In Review: Travel, Fitness, & Quality of Life]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Every year it is increasingly difficult to get these updates out in time. So it's a bit weird to be writing this 2024 year in review post while it's already halfway into 2025. I do like being able to write down my thoughts and reflections of the past year, so</p>]]></description><link>https://kalis.me/2024-year-in-review/</link><guid isPermaLink="false">6785483992718d063781ba1b</guid><dc:creator><![CDATA[Rosco Kalis]]></dc:creator><pubDate>Fri, 13 Jun 2025 12:45:48 GMT</pubDate><media:content url="https://kalis.me/content/images/2025/06/year-in-review-2024-1.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://kalis.me/content/images/2025/06/year-in-review-2024-1.jpg" alt="2024 Year In Review: Travel, Fitness, & Quality of Life"><p>Every year it is increasingly difficult to get these updates out in time. So it's a bit weird to be writing this 2024 year in review post while it's already halfway into 2025. I do like being able to write down my thoughts and reflections of the past year, so I'm publishing this review regardless.</p>
<p>In my previous year in review I wrote that 2023 had been a rather stable year with clear upward trajectory, in business as well as personally. That place of stability had been a very welcome change from the hectic years before. And this has allowed me to start focusing on some parts of life that had been neglected.</p>
<h2 id="revokecashentrepreneurship">💰 Revoke.cash &amp; Entrepreneurship</h2>
<p>It's been over 2 years now since I decided to work on Revoke.cash full-time, and 2024 has been the best year yet. We crossed the threshold of over 100 different crypto networks supported and grew the team beyond myself. We received a lot of support and public goods funding, and shipped loads of new features.</p>
<h3 id="rebranding">Rebranding</h3>
<p>While the clip-art logo that Revoke has had for the past years has served us well, it was time to retire the old brand in favour of one that <em>wasn't</em> made by me in about 10 minutes time.</p>
<img src="https://kalis.me/content/images/2025/03/brand.jpg" width="100%" alt="2024 Year In Review: Travel, Fitness, & Quality of Life">
<p>For the new brand and logo we worked together with <a href="https://schumanncombo.com/">Richard Schumann</a>, a Berlin-based designer that has provided us with very high quality work and an enjoyable collaboration. As part of the rebrand we also redesigned the browser extension, that was in dire need of some UI love.</p>
<img src="https://kalis.me/content/images/2025/03/extension.jpg" width="100%" alt="2024 Year In Review: Travel, Fitness, & Quality of Life">
<h3 id="batchrevoking">Batch Revoking</h3>
<p>One of the most requested features since Revoke's inception is the infamous <em>batch revoke</em>. And while our answer has always been that it is not possible to revoke multiple approvals in a single transaction, we have finally launched a UI overhaul that makes queuing up these individual transactions much easier.</p>
<img src="https://kalis.me/content/images/2025/03/batch-revoke.png" width="100%" alt="2024 Year In Review: Travel, Fitness, & Quality of Life">
<p>And with native account abstraction around the corner for Ethereum, chances are that it will finally also be possible to combine multiple revokes into a single transaction.</p>
<h3 id="riskindicators">Risk Indicators</h3>
<p>Another feature that had been on the backlog for quite some time was better risk indicators to offer guidance for which token approvals might be higher risk or even outright malicious.</p>
<img src="https://kalis.me/content/images/2025/03/risk-indicators.jpg" width="100%" alt="2024 Year In Review: Travel, Fitness, & Quality of Life">
<p>For this feature we partnered with <a href="https://scamsniffer.io">ScamSniffer</a> and <a href="https://webacy.com">Webacy</a>, and we've also built our own risk engine. I want to continue building out this risk engine to help users gain better insights into their token approvals.</p>
<h3 id="revenue">Revenue</h3>
<p>2024 has been a great year for public goods funding across the crypto ecosystem. We've received large grants from Optimism's Retro Funding, Gitcoin, Octant and ENS. This has allowed us to hire some part time contractors to help me out with the company.</p>
<img src="https://kalis.me/content/images/2025/05/optimism-retropgf.jpg" width="100%" alt="2024 Year In Review: Travel, Fitness, & Quality of Life">
<p>While revenue has been great, it is uncertain what 2025 will bring, which is unfortunately the nature of the business model that I'm trying to pursue as a public good. It may prove difficult, but I do want to try to continue building Revoke as a public good.</p>
<p>I've chatted with people at Optimism, and unfortunately it looks like Revoke will not be eligible for the next wave(s) of Optimism Retro Funding. I am grateful to them for their support in 2024, and it will be a big challenge to find a replacement for that revenue stream in 2025.</p>
<h2 id="sideprojects">👨‍🎨 Side Projects</h2>
<p>Side projects have always been a big part of what I do, but in the past years they have been becoming less prominent. Especially after Revoke.cash <em>graduated</em> from side project to main project. But some projects are still holding on strong and some new projects have emerged.</p>
<h3 id="cashscript">CashScript</h3>
<p>As I wrote in my previous year in review post, the Truffle Suite of developer tooling was sunset late 2023, and with it my popular open source truffle-related tools. So while I used to work on several open source developer tools in the past, only one remains: CashScript.</p>
<p>As a reminder, CashScript is a project that I created in 2019 as part of my Master's Thesis. Over the years it grew to the primary way to create smart contracts on Bitcoin Cash. And while my main focus is on the Ethereum ecosystem, it is great to see that CashScript is still growing, and I still dedicate some weekly time to it together with <a href="https://x.com/GeukensMathieu">Mathieu Geukens</a> who has been a part of the team since 2023.</p>
<img src="https://kalis.me/content/images/2025/05/cashscript-v10.jpeg" width="100%" alt="2024 Year In Review: Travel, Fitness, & Quality of Life">
<p>Its usage has kept rising in 2024, and while the BCH ecosystem is only a fraction of the size of Ethereum, it is very cool and rewarding to see that CashScript is being used to create dozens of novel DeFi applications, like derivatives platform <a href="https://anyhedge.com/">AnyHedge</a> and stablecoin <a href="https://www.moria.money/">Moria</a>.</p>
<h3 id="forestcomply">ForestComply</h3>
<p>Another side project that I've started this year is ForestComply. This is a product that came about after I spoke with my uncle Albert, who runs <a href="https://www.craftingmarkets.com/">Crafting Markets</a>, a company that imports and trades cacao beans.</p>
<img src="https://kalis.me/content/images/2025/05/forestcomply.jpg" width="100%" alt="2024 Year In Review: Travel, Fitness, & Quality of Life">
<p>The EU has developed new regulations surrounding the importing of cacao and coffee beans, timber, rubber, and several other commodities. Central to this new European Deforestation Regulation (EUDR) is that traders need to provide documentation that shows that their commodities do not come from deforested areas.</p>
<p>To help traders comply with this new regulation, my friend <a href="https://x.com/Steen3S">Dries</a> and I started ForestComply, a product that uses open source satellite data to detect deforestation in common origins of cacao and coffee. Our aim with this product was to provide a fairly priced usage-based alternative to some of the overly expensive solutions on the market.</p>
<img src="https://kalis.me/content/images/2025/05/olterterp.jpg" width="100%" alt="2024 Year In Review: Travel, Fitness, & Quality of Life">
<p>We spoke to potential users and developed a quite complete solution, and we even booked a week in a cabin to make sure we get all the important features implemented. But when the new regulation ended up being postponed by at least another year, we lost the motivation to continue working on it without a clear path to revenue.</p>
<p>We decided to shelve the project with the option to pick it back up if the regulation does end up being implemented, but we are dealing with the European Union, and with the current delays it would not surprise me if the regulation gets pushed even further or even completely removed.</p>
<h2 id="qualityoflife">💪 Quality of Life</h2>
<p>The past years have been very much focused on work: first my salaried &amp; freelance work, then building my own companies. As I've shared in my previous year in review posts, I've been gradually trying to find more balance in my life. And because Revoke has been steadily growing, I've been able to spend some more energy on other parts of life that had been somewhat neglected.</p>
<h3 id="aroundtheworldtrip">Around-the-World Trip</h3>
<p>Back in 2019 my wife Kiki and I flew around the world twice, which is an experience we've wanted to repeat for a long time, but never had the opportunity to. Because doing another long trip around the world is something that we've wanted to do for so long, we're very happy that we managed to make it happen in 2024.</p>
<img src="https://kalis.me/content/images/2025/06/mexico.jpg" width="100%" alt="2024 Year In Review: Travel, Fitness, & Quality of Life">
<p>We started our trip in February, which was exactly the right time - or maybe even a bit too late - to leave cold Amsterdam behind. Our plan was to just book one way tickets and figure out the next destination along the way. The first stop was driving through the Yucatán peninsula in Mexico.</p>
<p>From there we continued to Hawaii, a place that we also first visited in 2019 on our previous round-the-world trip. When we first visited Hawaii we were still students, so our budget didn't allow for a whole lot of exploration. We made up for that this time around and saw a lot of Oahu in two weeks.</p>
<img src="https://kalis.me/content/images/2025/06/hawaii.jpg" width="100%" alt="2024 Year In Review: Travel, Fitness, & Quality of Life">
<p>Surprisingly, we were able to get tickets from Hawaii to Melbourne, Australia for just $250 (for a 12 hour flight), so that decision was easily made. We planned to spend a few weeks in Australia, driving from Melbourne to Sydney to Brisbane.</p>
<p>We quickly realised that Australia warrants a much longer stay, so we ended up staying in the country for 5 weeks, which still did not seem nearly enough.</p>
<img src="https://kalis.me/content/images/2025/06/australia.jpg" width="100%" alt="2024 Year In Review: Travel, Fitness, & Quality of Life">
<p>After Australia we got in touch with a friend that we met in Hawaii on our previous trip in 2019 who had since moved to Indonesia. He told us to come over to Sumbawa, so that was our next stop. Unfortunately he ended up not replying once we got to the island, so we didn't get to meet up with him.</p>
<p>Nonetheless, we met some really amazing locals in Sumbawa that made us feel right at home and even invited us into their homes and cooked some delicious meals for us. The nature in Sumbawa was also beautiful, but overall we did miss a bit of the bustle of our other, busier destinations.</p>
<img src="https://kalis.me/content/images/2025/06/indonesia.jpg" width="100%" alt="2024 Year In Review: Travel, Fitness, & Quality of Life">
<p>In the mean time we had gotten in touch with Håkon, another friend that we'd met on our previous trip in 2019, who happened to also be traveling through South East Asia at the same time. We decided to fly to Bali to hang out with him, and we had a blast with him and met a bunch of other awesome people as well.</p>
<p>From Bali we flew to Singapore to meet family that I had never met before, before making the trip back to Europe with stops in Turkey and Macedonia to visit Kiki's family and our friends Stef and Ana.</p>
<img src="https://kalis.me/content/images/2025/06/singapore-turkey.jpg" width="100%" alt="2024 Year In Review: Travel, Fitness, & Quality of Life">
<p>This trip is something we'd wanted to do for a long time, so I'm grateful that we had the opportunity to do so in 2024. I was able to continue working on Revoke while travelling, although not as much as I was planning to.</p>
<p>I would like to be able to do another trip like this in the future, but it remains a challenge to combine long travels with work.</p>
<h3 id="healthfitness">Health &amp; Fitness</h3>
<p>When I was still studying, I exercised a lot and was in great shape. I cared about eating healthy and I rowed 5 times a week. But in the past years, I had slowly stopped caring about fitness. The pandemic played a role in this, but I also just didn't really have a lot of motivation to work out.</p>
<p>Knowing that it was hard to get back into the habit of working out, I decided to give personal training a go. I was introduced a personal trainer in Amsterdam and I started training with him twice a week after getting back from our travels.</p>
<p>This has made a big impact on my day to day life: I feel much fitter and healthier, and I have a lot more energy to spend on other aspects of life. After experiencing the difference that a relatively small amount of exercise can make, I will make a more conscious effort to keep that going.</p>
<h3 id="socialhobbies">Social &amp; Hobbies</h3>
<p>Another field where I've been looking to improve is spending more quality time with people I care about. So that means more dedicated time with family, friends and my wife. In 2024, we made an effort to be more aware of this and go out of our way to plan that time and also meet up spontaneously more often.</p>
<p>Because we travelled quite a bit this year, it wasn't always possible to see our loved ones as much as we'd initially had intended. But during our travels, we did make an effort to visit friends across the world where possible, such as our friends in Mexico, Macedonia and the United States.</p>
<img src="https://kalis.me/content/images/2025/06/games.jpg" width="100%" alt="2024 Year In Review: Travel, Fitness, & Quality of Life">
<p>This year I also decided to make an effort to play more board games, which is a hobby that I've been wanting to get into more. So <a href="https://x.com/gnidan">gnidan</a> inspired me to install the bgstats app, which I've been using to keep track of my board games. I've been having a lot of fun and I'm making a conscious effort to try to meet up for board games more often.</p>
<h2 id="2025">2025</h2>
<p>In 2024 Revoke.cash has done well, which has allowed me to focus a bit on things outside of work. While I've reached all of my fitness and travel goals, I still do want to spend more time with people I care about.</p>
<p>2025 looks like it may not be as smooth sailing from a business perspective. So I hope to be able to continue making Revoke.cash a success without taking away from the positive steps I have been taking to improve my life outside of work.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[2023 Year In Review: Entrepreneurship, Making Money & Stability]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>While the past couple of years have been hectic, 2023 feels like it has been surprisingly steady - and has a clear upward trajectory. The year before felt like a roller coaster with incredible highs but also deep lows. That is why stability is a welcome change - and in</p>]]></description><link>https://kalis.me/2023-year-in-review/</link><guid isPermaLink="false">6571c2984ab8d902cfe40270</guid><category><![CDATA[life]]></category><category><![CDATA[year-review]]></category><dc:creator><![CDATA[Rosco Kalis]]></dc:creator><pubDate>Thu, 07 Mar 2024 15:51:00 GMT</pubDate><media:content url="https://kalis.me/content/images/2024/03/2023-year-in-review-1.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://kalis.me/content/images/2024/03/2023-year-in-review-1.jpg" alt="2023 Year In Review: Entrepreneurship, Making Money & Stability"><p>While the past couple of years have been hectic, 2023 feels like it has been surprisingly steady - and has a clear upward trajectory. The year before felt like a roller coaster with incredible highs but also deep lows. That is why stability is a welcome change - and in line with the goals I set a year ago.</p>
<h2 id="amsterdamindiehackeroffice">💼 Amsterdam Indie Hacker Office</h2>
<p>In 2022, my friend Dries and I rented an office for Chaingrep. After Chaingrep fell apart, Dries and I decided to keep the office to work on our own projects. But we had to leave the office in March of 2023, after which we had to go back to working from home. Dries and I met up to work from coffee shops a few days a week, but we missed having an office to go to.</p>
<p>Enter the Amsterdam Indie Hacker Office. In May 2023 I stumbled upon <a href="https://www.indiehackers.com/post/how-to-hack-hacker-news-and-consistently-hit-the-front-page-56b4a04e12">this article</a> from Simple Analytics, the analytics company I use for my projects. The article had a link to the Twitter account of its author <a href="https://twitter.com/IronBrands16">Iron Brands</a> (real name 😉).</p>
<img src="https://kalis.me/content/images/2024/02/amsterdam-indie-hacker-office.jpg" width="90%" alt="2023 Year In Review: Entrepreneurship, Making Money & Stability">
<p>As luck would have it, Simple Analytics has its office in Amsterdam and was renting out some desks. So after a quick meet and greet, Dries and I moved into this little office filled with other indie hackers and entrepreneurs.</p>
<p>Since then I've learned a lot from the other people in the office and Dries even started a new business together with some of the guys from the office! Having the office to go to and work from has been really awesome, and I would not want to go back to WFH.</p>
<h2 id="revokecashentrepreneurship">💰 Revoke.cash &amp; Entrepreneurship</h2>
<p>In 2022 I decided to start working on Revoke.cash full-time and in 2023 this really came to fruition. I've been steadily working on improving the platform and it has seen nice growth over the course of the year.</p>
<h3 id="redesign">Redesign</h3>
<p>The new year started strong with the launch of the redesigned version of Revoke.cash. While the old version had seen some updates since the initial 2019 launch, it was largely the same as when Revoke.cash started. So it felt quite dated and hard to add new features.</p>
<div style="display: flex;">
    <img src="https://kalis.me/content/images/2023/01/revoke-old.png" style="width: 50%" alt="2023 Year In Review: Entrepreneurship, Making Money & Stability">
    <img src="https://kalis.me/content/images/2023/01/revoke-new.png" style="width: 50%" alt="2023 Year In Review: Entrepreneurship, Making Money & Stability">
</div>
<p>With the 2023 refresh, I updated the platform to make it easier to use <em>and</em> easier for me to add additional functionality in the future. But while I wanted the website to feel more modern, I also didn't want to give up the &quot;signature&quot; black &amp; white look of Revoke.cash. I think I largely succeeded.</p>
<h3 id="makingmoney">Making Money</h3>
<p>Since I no longer had a job to help sustain myself and my wife, making money was a high priority. When I started working full time on Revoke.cash in Q4 of 2022, my intention was to add paid premium features to generate an income from Revoke.cash.</p>
<p>A big part of the motivation behind the redesign was also to make it easier to add this kind of functionality by adding more structure to the website. But I found myself spending more time on improving the free product instead of adding paid features.</p>
<h4 id="sponsorships">Sponsorships</h4>
<p>Early in 2023 this meant that funding was running very low, and to solve the problem at hand, I considered adding advertisements to the free product as a sort of stopgap solution. However, I did not want to make it an intrusive part of the application.</p>
<p>So rather than traditional advertisements, I decided to see if there were any companies that were interested in more of a &quot;sponsorship&quot; deal, which meant adding sponsor banners to a dedicated section of our landing page / website, without cluttering the actual application UI.</p>
<img src="https://kalis.me/content/images/2024/02/sponsors.jpg" width="90%" alt="2023 Year In Review: Entrepreneurship, Making Money & Stability">
<p>I launched this sponsorships program in February of 2023, and thanks to the help of the initial set of sponsors (<a href="https://boringsecurity.com/">Boring Security</a>, <a href="https://premint.xyz/">PREMINT</a>, <a href="https://vulcan.xyz/">Vulcan</a> and <a href="https://earni.fi/">Earni.fi</a>) I was able to extend the runway needed to continue working on Revoke.cash.</p>
<h4 id="publicgoodsfunding">Public Goods Funding</h4>
<p>The initial set of sponsorships helped with extending the runway, but funds were still running lower and the first months of 2023 were pretty stressful. But then something amazing started to happen: Public Goods Funding took off for Revoke.cash.</p>
<p>While we had received a decent amount of donations and grants through Gitcoin Grants already in 2022, 2023 saw a lot more interest in public goods funding. Gitcoin Grants transitioned to its new decentralised system and ran several alpha / beta rounds that brought in funding.</p>
<img src="https://kalis.me/content/images/2024/02/gitcoin-grants.jpg" width="90%" alt="2023 Year In Review: Entrepreneurship, Making Money & Stability">
<p>And it was no longer only Gitcoin that was leading the public goods funding effort. In 2023, Optimism launched a massive effort into funding public goods through their RetroPGF program, ENS started a public goods funding / grants program, and the Octant platform sprung up from what used to be the Golem community.</p>
<img src="https://kalis.me/content/images/2024/02/retropgf.jpg" width="100%" alt="2023 Year In Review: Entrepreneurship, Making Money & Stability">
<p>Overall 2023 saw a lot more focus on funding public goods throughout the Ethereum / crypto ecosystem. While it felt hard to depend on these kinds of programs at first, it feels like they are maturing into a viable way to build free and open source software in the crypto industry.</p>
<p>I think it is pretty amazing that the crypto ecosystem has been able to create this kind of culture that <em>wants</em> to fund public goods. Crypto is pretty unique in that aspect, and I think a lot of it is due to the unique financial incentives that exist in crypto.</p>
<p>In the crypto world, it is possible for people to produce positive externalities by funding public goods, while still personally benefiting enough from funding those public goods. This means that we don't <em>just</em> need to rely on the goodness of people's hearts - although that goodness is definitely still much appreciated.</p>
<h3 id="growingtheteam">Growing The Team</h3>
<p>Around the end of 2023, I ended up in a spot where it looks like Public Goods Funding is a viable revenue model for Revoke.cash and there is still plenty of stuff to build. With that in mind I felt comfortable to slowly expand the team beyond just myself. So in December I hired my friend Dries for a part time software engineering role.</p>
<img src="https://kalis.me/content/images/2024/02/revoke-team.png" width="90%" alt="2023 Year In Review: Entrepreneurship, Making Money & Stability">
<p>Dries had been helping out as a contractor on several features earlier in the year, such as the new exploit checker. He's currently working on expanding the features around risk insights, and I'm sure he'll contribute a bunch more in 2024.</p>
<h3 id="exploitchecker">Exploit Checker</h3>
<p>In April of 2023, the crypto world was shaken by an exploit targeting popular DEX SushiSwap. In this hack, a bug in SushiSwap's new router contract was exploited to steal approved user funds.</p>
<p>Because SushiSwap is such an established player, no one expected such an exploit. So this was a wake up call for many people in the space to stay on top of their token approvals. When such an event happens, people rush to Revoke.cash to see if they are affected. But if a user has many token approvals, it can be hard to identify the affected approval. This is what inspired the new Exploit Checker.</p>
<img src="https://kalis.me/content/images/2024/02/sushiswap-exploit-checker.png" width="90%" alt="2023 Year In Review: Entrepreneurship, Making Money & Stability">
<p>I asked my friend Dries to help out as a contractor, and he had the exploit checker implemented in no time. We set it up in such a way that it's super  simple to add new checkers whenever exploits happen in the future so that people can easily check whether they have anything to revoke.</p>
<h3 id="educationalcontent">Educational Content</h3>
<p>Revoke.cash is a great tool to help people stay safe in crypto. But in order to fully stay safe, users also need to be more educated regardless of tooling. To help with that I started writing more educational content on Revoke.cash.</p>
<p>To do so, I created the Learn section / knowledgebase as well as a blog. On the blog I try to post product updates or other event-driven updates about the crypto ecosystem. On the Learn section, I am writing evergreen educational content about crypto and security.</p>
<p>In total, I created a few dozen educational articles and blog posts, so to have a professional illustrator create custom cover images for all of them could become expensive.</p>
<img src="https://kalis.me/content/images/2024/02/cover-images.jpg" width="90%" alt="2023 Year In Review: Entrepreneurship, Making Money & Stability">
<p>With the recent hype around AI, I figured I could try to have ChatGPT generate cover images instead. In the end this took a lot of trial and error, and it definitely wasn't as automated as I'd have preferred, but I think the results ended up great.</p>
<h3 id="permit2approvals">Permit2 Approvals</h3>
<p>In the world of token approvals, there is a concept of &quot;Permit&quot; signatures. This is a standard to give token approvals using off-chain signatures, rather than requiring on-chain transactions. This is more user-friendly, since a user does not have to send two separate transactions when swapping tokens.</p>
<img src="https://kalis.me/content/images/2024/02/permit2-approvals.png" width="90%" alt="2023 Year In Review: Entrepreneurship, Making Money & Stability">
<p>One drawback of these signatures is that only a small subset of tokens supports it. To mitigate that, Uniswap came up with a system they call &quot;Permit2&quot;, which enables gasless Permit signatures for <em>every</em> ERC20 token. Championed by Uniswap, Permit2 gained traction in 2023, which meant that it required support on Revoke.cash as well.</p>
<h3 id="riskinsights">Risk Insights</h3>
<p>With a bunch of improvements implemented in the past year, one of the biggest topics on the roadmap is providing users with more actionable insights regarding their token approvals. Right now a user logs onto Revoke.cash and is greeted with a big list of token approvals, but it is hard for them to understand what they all mean.</p>
<img src="https://kalis.me/content/images/2024/02/wallet-health.jpg" width="90%" alt="2023 Year In Review: Entrepreneurship, Making Money & Stability">
<p>To help users make better decisions for their wallets, I want to provide them with actionable data. In 2023 I already started this by integrating <a href="https://nefture.com/">Nefture</a>'s wallet health score into Revoke.cash and by integrating pricing information for fungible tokens - making it easier to see how much value is at risk.</p>
<p>This is a great start, but there is still a lot to be done in 2024. First order of business is adding NFT pricing data, since the current pricing data only supports fungible tokens. From there I want to add much more fine-grained risk insights that explain which token approvals are risky and why.</p>
<h3 id="ledgerconnectkithack">Ledger Connect Kit Hack</h3>
<p>While most of 2023 was very positive, halfway into December, we did see a very regrettable event in the Ledger Connect Kit Hack. In this incident, a popular library created by Ledger was compromised and malicious actors were able to inject malicious transactions on websites that used this library - including Revoke.cash.</p>
<p>While the issue was resolved quickly, this event still sent shockwaves throughout the crypto ecosystem, since many popular websites were affected at the same time. Including Revoke.cash, a website that many people visit <em>especially</em> at times like these. Thankfully, Ledger has pledged to compensate all affected users.</p>
<h2 id="opensource">🐙 Open Source</h2>
<img src="https://kalis.me/content/images/2024/01/star-history.jpg" width="90%" alt="2023 Year In Review: Entrepreneurship, Making Money & Stability">
<p>As in previous years, my Open Source developer tools were still a pretty important aspect of my 2023. But 2023 also brought some changes.</p>
<h3 id="trufflesunset">Truffle Sunset</h3>
<p>In the summer Consensys, the company behind development suite Truffle, announced that they would be sunsetting Truffle by the end of the year. Over the years other development tools like Hardhat and Foundry had been taking a lot of market share from Truffle, but its deprecation will still be felt in the Ethereum ecosystem.</p>
<p>I had been maintaining a few libraries that were built on top of Truffle: <code>truffle-plugin-verify</code> and <code>truffle-assertions</code>. With Truffle shutting down its services, I decided to also archive these projects at the end of 2023.</p>
<p>It feels a bit bitter shutting down these projects after maintaining them for many years. But I've also talked about time management in previous year review posts and it is definitely a welcome change to have fewer projects on my hands.</p>
<h4 id="gratitude">Gratitude</h4>
<p>Truffle has played a huge part in my crypto journey. I used it for creating my first smart contracts back in 2018, and TruffleCon 2018 was the first conference I ever spoke at. And of course it spawned my own Truffle-adjacent libraries that were used by thousands of projects over the years and have gotten over a million downloads.</p>
<img src="https://kalis.me/content/images/2024/01/downloads.png" width="90%" alt="2023 Year In Review: Entrepreneurship, Making Money & Stability">
<p>Then I've done contracting work for Truffle on a few super cool features, like the Truffle Dashboard. I've met some great friends through my work on Truffle and my own libraries. So I'm very thankful for the role that Truffle played here.</p>
<p>And of course I want to thank everyone that has used my libraries over the past 5 years and everyone that has contributed to them in that time. In 2023, I specifically want to thank <a href="https://github.com/gnidan">gnidan</a>, <a href="https://github.com/kuzdogan">Kaan Uzdoğan</a>, <a href="https://github.com/d10r">d10r</a>, <a href="https://github.com/whyvrafvr">whyvrafvr</a> and <a href="https://github.com/zhangtianhao">zhangtianhao</a> for their open source contributions to <code>truffle-plugin-verify</code> in its final year.</p>
<h3 id="cashscript">CashScript</h3>
<p>Besides my Truffle-related libraries, my other big open source developer tool is CashScript, the smart contract programming language for BCH that I created during my time at Bitcoin.com in 2019. The Bitcoin Cash ecosystem has been adding new smart contract functionality and in 2023 adoption of CashScript has been increasing.</p>
<p>In 2023, Bitcoin Cash added a native token system called CashTokens that enables fungible and non-fungible tokens on Bitcoin Cash as well as expanding general smart contract capabilities. To support these new tokens in CashScript, <a href="https://twitter.com/GeukensMathieu">Mathieu Geukens</a> joined the CashScript team.</p>
<img src="https://kalis.me/content/images/2024/03/tokenaut.png" width="90%" alt="2023 Year In Review: Entrepreneurship, Making Money & Stability">
<p>With CashTokens implemented, many new smart contract projects were created. These applications were much more sophisticated than most BCH contracts that were created in earlier years. This made us realise that we needed to make it easier to develop these more advanced applications.</p>
<p>As a first step towards this, we created a better, more advanced transaction builder in the SDK to assist with more complex transactions. Shortly after, <a href="https://twitter.com/mainnet_pat">mainnet_pat</a> helped expand CashScript's debugging functionality with exciting new features. We still need to polish these features and we have some more exciting things planned to help CashScript developers create the best smart contracts they can think of.</p>
<h2 id="travelevents">🛩️ Travel &amp; Events</h2>
<p>With COVID, we hadn't been able to travel as much as we'd have liked in the past years, so we had a lot to make up for. We started 2023 strong with a month-long trip to visit our friend Jenny in New York City and our friends Nick and almost in Philadelphia. The plan was to work and chill, but that turned out to be hard to combine. Nonetheless we had a good time!</p>
<p>Initially we were planning to travel some more in the winter, but after returning from NYC we were happy to be home for a while, so the next trip had to wait until summer.</p>
<img src="https://kalis.me/content/images/2024/03/prague-defi-summit.jpg" width="90%" alt="2023 Year In Review: Entrepreneurship, Making Money & Stability">
<p>In May I travelled to Prague to speak at the Prague DeFi Summit and to judge at the ETHPrague hackathon. I had a great time there as it was my first time being a judge in a hackathon, and ETHPrague used a novel judging system based on quadratic voting.</p>
<p>Then in summer it was time for a proper holiday. That meant max 1 hour of work each day, sleeping in, going to the beach and eating amazing food. We returned to our favourite little spot in Sardinia that we'd been before. But this time we brought some of our friends to share the experience.</p>
<img src="https://kalis.me/content/images/2024/03/italy.jpg" width="90%" alt="2023 Year In Review: Entrepreneurship, Making Money & Stability">
<p>After Sardinia, we continued with a month of working remotely from Gran Canaria. Chilling + working was a lot easier to combine this time around, and I suspect the nice weather had a lot to do with it. So our next winter trip will definitely not be to a cold place like NYC again.</p>
<p>Then during fall our friends Nick and almost were getting engaged and they invited us to their engagement party in Philadelphia, after which we stayed with them for a while longer. This time around we had a lot more time to explore and enjoy Philadelphia and some of its surroundings, which was lots of fun.</p>
<p>After returning home for just a few weeks, it was time for our final trip of the year, for the much-anticipated Devconnect conference in Istanbul. There I met up with a bunch of friends and we celebrated Kiki's birthday at Nicole, an amazing restaurant in the city.</p>
<p>2023 was definitely a year with some nice trips, but next year I feel like we should be a bit more strategic about them. So that means that it's probably nicer to stay in Amsterdam during the summer and not visit too many cold destinations in the winter.</p>
<h2 id="2024">📈 2024</h2>
<p>While 2022 had high highs and low lows, 2023 brought a lot more stability and steadiness. Now, at the start of 2024 I feel like I am in a good place to continue building on the foundations that were laid in the past year. I'm grateful for everything that happened in 2023 and looking forward to 2024.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[🎢 2022 Year In Review: Getting Married, Building Startups & Finding Balance]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>While 2021 saw the heights of the cryptocurrency market, 2022 kicked off its decline. And by now we're definitely back in a crypto bear market. Not only the crypto market is affected though, since the entire global economy is in recession.</p>
<p>2022 was a turbulent year with a lot of</p>]]></description><link>https://kalis.me/2022-year-in-review/</link><guid isPermaLink="false">6387db36ae9c2b25352274c9</guid><category><![CDATA[life]]></category><category><![CDATA[year-review]]></category><dc:creator><![CDATA[Rosco Kalis]]></dc:creator><pubDate>Sun, 29 Jan 2023 22:56:31 GMT</pubDate><media:content url="https://kalis.me/content/images/2023/01/rollercoaster.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://kalis.me/content/images/2023/01/rollercoaster.jpg" alt="🎢 2022 Year In Review: Getting Married, Building Startups & Finding Balance"><p>While 2021 saw the heights of the cryptocurrency market, 2022 kicked off its decline. And by now we're definitely back in a crypto bear market. Not only the crypto market is affected though, since the entire global economy is in recession.</p>
<p>2022 was a turbulent year with a lot of ups and downs, both personally and globally. As the year is coming to an end I feel like I'm back in a similar spot as where I started, albeit with a lot of new experiences under my belt and a lot of new people met.</p>
<h2 id="gettingmarried">💍 Getting Married</h2>
<p>In 2021 my wife (then girlfriend) told me that if we were to get married, it had to be on a palindrome date. And I did not want to wait for the next good palindrome in 2030. So 22-02-2022 it was.</p>
<p>There were still a lot of COVID rules in place, so we just had a tiny ceremony with a few of our close friends at city hall at 10 in the morning. Afterwards we had lunch at an amazing restaurant, and we finished the day at the beach. We had a great time. For a while it still felt weird to say &quot;my wife&quot;, but it's slowly starting to feel more natural.</p>
<h2 id="armadachaingrep">🚀 Armada &amp; Chaingrep</h2>
<p>I ended my previous year in review by looking forward at the months to come and introducing Armada, the startup that I was working on with my friend Merwane. Needless to say, this new startup played a big role in my past year.</p>
<h3 id="ycombinator">Y Combinator</h3>
<p>At the start of 2022, Armada was accepted into the W22 batch of Y Combinator, the world's premier startup accelerator. This comprises a three month program in which YC helps you make the most of your startup journey.</p>
<p>The program includes weekly meetings, presentations from the founders of big YC companies - such as Airbnb or Stripe - and tailored advice from the YC partners. The partners are generally also accomplished founders and experts in their fields.</p>
<p>In prior years, YC batches had been in-person at Y Combinator's Bay Area campus, but due to COVID restrictions, the past couple of batches have all been virtual. And while being there in-person would have made it even better, participating in YC has been one of my coolest experiences to date.</p>
<h3 id="pivaaat">PIVAAAT!</h3>
<p>As many startup founders know, chances are that your first idea isn't the one that will eventually stick. And while we enjoyed working on our idea for a natural language NFT search engine, we found that was a bit too narrow an idea to build a business on.</p>
<img src="https://kalis.me/content/images/2023/01/armada-punks.png" width="90%" alt="🎢 2022 Year In Review: Getting Married, Building Startups & Finding Balance">
<p>So over the months we pivoted several times. We shifted from Armada to Chaingrep, which we described as a &quot;block explorer for humans&quot;. This later morphed into a developer-targeted API for human-readable transactions.</p>
<img src="https://kalis.me/content/images/2023/01/insider-chaingrep.png" width="90%" alt="🎢 2022 Year In Review: Getting Married, Building Startups & Finding Balance">
<p>After working on that for some months we found ourselves questioning our product again. At this point I was feeling pretty burnt out from all the pivoting while we hadn't actually shipped anything in all those months.</p>
<p>At the same time, Merwane and I had a pretty different outlook on where we wanted to go with the company. So ultimately we decided to split up and go our own ways.</p>
<h3 id="aftermath">Aftermath</h3>
<p>For about 8 months I worked very intensively on trying to build a startup. We went through YC and we raised VC funding. And then it was over. When it happened I had very mixed feelings. Disappointed that we hadn't been able to make it work, but also relieved to take a break from the grind.</p>
<p>I promised myself to take a break at least until October. In that time I went on a few trips abroad and spent some time working on projects, but mainly tried to take it easy.</p>
<img src="https://kalis.me/content/images/2023/01/norway.jpg" width="90%" alt="🎢 2022 Year In Review: Getting Married, Building Startups & Finding Balance">
<p>It's always hard for me to take a full break without doing any kind of work. So I didn't fully succeed at my break, but I definitely did have some weeks where I didn't do any work at all and was able to touch grass in nature.</p>
<p>Now October has come to pass, which means that the time of taking it easy has ended and I'm back to work. Now I'm working my own project again, albeit in a different way: this time as an indie hacker.</p>
<h2 id="revokecash">🛡 Revoke.cash</h2>
<p>Early February we saw a very high-profile phishing scam, startling the entire crypto space. Many people were scared that OpenSea was compromised, which thankfully wasn't the case. But the chaos of this incident brought so much traffic to Revoke.cash that it went down while I was sleeping.</p>
<img src="https://kalis.me/content/images/2023/01/revoke-cash-analytics.png" width="90%" alt="🎢 2022 Year In Review: Getting Married, Building Startups & Finding Balance">
<p>During the first parts of the year I was mainly focused on building Chaingrep, so I didn't spend a lot of time working on Revoke.cash. But after leaving the startup I decided to see how far I could take it.</p>
<h3 id="browserextension">Browser Extension</h3>
<p>Shortly after leaving Chaingrep I built the Revoke.cash browser extension, a kind of &quot;wallet firewall&quot; that pops up with warnings whenever you're about to do something potentially harmful. This is an effective way of combating phishing scams, since phishing websites always try to make you sign something you shouldn't.</p>
<img src="https://kalis.me/content/images/2023/01/extension-screenshot-1.png" width="90%" alt="🎢 2022 Year In Review: Getting Married, Building Startups & Finding Balance">
<p>The browser extension quickly gained over 10k weekly users, and is still growing. There are several similar projects, which definitely validates the idea. But ultimately I can imagine this kind of functionality being directly integrated into a wallet such as MetaMask.</p>
<h3 id="multichainsupport">Multi-chain support</h3>
<p>In earlier years I had tried to expand Revoke.cash' functionality to more chains, but never managed to support more than a handful of chains. But with an ever-growing number of chains, it was necessary to support more than just a few.</p>
<p>In 2022 I received grants from RSK and Harmony, which helped realise Revoke.cash' extended multi-chain support. Throughout the year, the number of chains grew, and today more than 30 different chains are supported.</p>
<h3 id="multiwalletsupport">Multi-wallet support</h3>
<p>While MetaMask is the largest browser wallet, it definitely isn't the only one, and there's also a growing ecosystem of mobile and desktop wallets. So only supporting MetaMask meant I was missing out on serving a lot of users.</p>
<p>One of Revoke.cash' big strengths is its open source code allowing anyone to contribute to it, so WalletConnect and Coinbase Wallet support was added by contributor <a href="https://github.com/thevolcanomanishere">Alex McGonagle</a>. I added Gnosis Safe support to that as well, so now Revoke.cash essentially supports every popular wallet on the market.</p>
<h3 id="localisation">Localisation</h3>
<p>When I was at Devcon in Bogota last October I was confronted with the fact that a lot of people do not speak English <em>at all</em>. To better serve these people I decided it would be good to translate the website. I reached out to some people and got the website and extension translated into Spanish and Chinese.</p>
<p>Looking back, I think the Chinese translation has been very succesful, since a large part of the user base speaks Chinese, and this segment of users grew the most since adding translations. But I'm not sure if the Spanish translation has been worth the effort. Spanish speakers are still only a tiny percentage of users, despite being one of the biggest languages in the world.</p>
<h3 id="redesign">Redesign</h3>
<p>Now at the start of 2023 I just launched a big overhaul of the website's UI. The old Revoke.cash was created in 2019, and while it has received updates over the years, the UI wasn't the greatest or most flexible.</p>
<div style="display: flex;">
    <img src="https://kalis.me/content/images/2023/01/revoke-old.png" style="width: 50%" alt="🎢 2022 Year In Review: Getting Married, Building Startups & Finding Balance">
    <img src="https://kalis.me/content/images/2023/01/revoke-new.png" style="width: 50%" alt="🎢 2022 Year In Review: Getting Married, Building Startups & Finding Balance">
</div>
<p>So with this redesign I hope to make Revoke.cash more accessible to a larger group of users, and I want to make it easier to add more features in the future. My friend Jenny has been a great help with the UX design aspects of the new website.</p>
<h2 id="facefilterai">🤖 FaceFilter.AI</h2>
<p>A big theme in 2022 was AI. We saw the advent of DALL-E, a magic AI powered art generator. But as quickly as DALL-E emerged, just as quickly was it supplanted by the open-source alternative Stable Diffusion.</p>
<div style="display: flex; gap: 4px;">
    <img src="https://kalis.me/content/images/2023/01/facefilter-viking.png" style="width: calc(25% - 3px)" alt="🎢 2022 Year In Review: Getting Married, Building Startups & Finding Balance">
    <img src="https://kalis.me/content/images/2023/01/facefilter-neon.png" style="width: calc(25% - 3px)" alt="🎢 2022 Year In Review: Getting Married, Building Startups & Finding Balance">
    <img src="https://kalis.me/content/images/2023/01/facefilter-peaky.png" style="width: calc(25% - 3px)" alt="🎢 2022 Year In Review: Getting Married, Building Startups & Finding Balance">
    <img src="https://kalis.me/content/images/2023/01/facefilter-charcoal.png" style="width: calc(25% - 3px)" alt="🎢 2022 Year In Review: Getting Married, Building Startups & Finding Balance">
</div>
<p>To dive more into AI, my friend Dries and I created <a href="https://facefilter.ai">FaceFilter.AI</a>, an AI-powered profile picture generator. We were inspired by other apps such as Pieter Levels' <a href="https://avatarai.me">AvatarAI</a> and Danny Postma's <a href="https://profilepicture.ai">ProfilePicture.AI</a>. But we added the functionality to write your own &quot;prompts&quot; so that you can go wild with your own creativity.</p>
<h2 id="imperfectionsbykalis">🖼 Imperfections by Kalis</h2>
<p>Last year I shared <a href="https://bykalis.com"><em>Imperfections by Kalis</em></a>, the generative art project I was working on with my dad. In 2022 we finished the project and launched it on Art Blocks, one of the biggest platforms for generative art NFTs.</p>
<img src="https://kalis.me/content/images/2022/01/imperfections-by-kalis.jpg" width="90%" alt="🎢 2022 Year In Review: Getting Married, Building Startups & Finding Balance">
<p>We organised a small launch event at the <a href="https://www.openspace.wtf/">OP.ENSPACE gallery</a> in Amsterdam, where we watched as people minted their NFTs. It took a few weeks after the initial launch, but we ended up selling all 450 pieces.</p>
<p>After the launch I created a <a href="https://github.com/rkalis/p5-typescript-starter">GitHub template repository</a> with all the boilerplate code that I used during the development of the generative art scripts. So if you're looking to experiment with generative art yourself, be sure to check it out!</p>
<h2 id="opensource">🐙 Open Source</h2>
<p>While Revoke.cash' popularity soared this year, the usage of my open source developer tooling only grew slightly, peaking at 50k+ monthly downloads. They did reach a different milestone though: 1M combined lifetime downloads!</p>
<img src="https://kalis.me/content/images/2023/01/npm-download-stats-2022.png" width="100%" alt="🎢 2022 Year In Review: Getting Married, Building Startups & Finding Balance">
<p>I shipped some big updates to my open source projects this year. The biggest update was adding support for Sourcify to truffle-plugin-verify. Sourcify is a repository of verified contract source code similar to Etherscan's - but fully open-source. I've been wanting to add Sourcify support for years already, so this was long overdue.</p>
<img src="https://kalis.me/content/images/2023/01/star-history-20221231--1-.png" width="90%" alt="🎢 2022 Year In Review: Getting Married, Building Startups & Finding Balance">
<p>The strength of open source is in its contributors. So for Revoke.cash I want to thank <a href="https://github.com/bossyuansu">bossyuansu</a> and <a href="https://github.com/maorstamati">maorstamati</a> for adding new chains, <a href="https://github.com/simbirskiy">Alexey Simbirskiy</a>, <a href="https://github.com/msvab">Michal Šváb</a> and <a href="https://github.com/chawyehsu">Chawye Hsu</a> for adding new address labels, <a href="https://github.com/dawsbot">Dawson Botsford</a> for improving website performance, <a href="https://twitter.com/Dmars300">Diego Mares</a> and <a href="https://twitter.com/crypto_cindy">Cindy Wang</a> for their translations, and most of all <a href="https://github.com/thevolcanomanishere">Alex McGonagle</a> for adding WalletConnect support.</p>
<p>For truffle-plugin-verify I want to thank <a href="https://github.com/albertov19">albertov19</a>, <a href="https://github.com/niccolopetti">Niccolò Petti</a>, <a href="https://github.com/nozeroctnellav7">nozeroctnellav7</a>, <a href="https://github.com/alebanzas">Alejandro Banzas</a>, <a href="https://github.com/d10r">d10r</a> and <a href="https://github.com/sebastian-baier">Sebastian Baier</a> for adding new chains, and <a href="https://github.com/denizsurmeli">Deniz Surmeli</a>, <a href="https://github.com/fuchengshun">fuchengshun</a>, <a href="https://github.com/Mukundan314">Mukundan Senthil</a>, <a href="https://github.com/akshaydevh">akshaydevh</a> and <a href="https://github.com/d10r">d10r</a> for contributing new features. And most of all I want to thank <a href="https://github.com/kuzdogan">Kaan Uzdoğan</a> and <a href="https://github.com/marcocastignoli">Marco Castignoli</a> from the Sourcify team to help realise the Sourcify integration!</p>
<p>For CashScript I want thank <a href="https://github.com/prashantpawar">Prashant Singh Pawar</a> and <a href="https://github.com/mainnet-pat">mainnet-pat</a> for fixing bugs, <a href="https://github.com/bitjson">Jason Dreyzehner</a> for refactoring internal code, <a href="https://github.com/mr-zwets">Mathieu Geukens</a> for his work on CashTokens and other improvements and <a href="https://github.com/nathanielCherian">Nathaniel Cherian</a> for adding new features.</p>
<h2 id="events">🛩️ Events</h2>
<p>While crypto events are always fun, they do also tend to take away significant time away from work. And because this year was heavy on work I had to really prioritise the events I wanted to visit. And I knew that the one event I couldn't miss this year was Devcon VI in Colombia.</p>
<img src="https://kalis.me/content/images/2023/01/devcon.jpg" width="60%" alt="🎢 2022 Year In Review: Getting Married, Building Startups & Finding Balance">
<p>Because of COVID, it had been 3 years since the last installment of Devcon. Devcon V was the first edition I attended, so I was excited to attend my second one. So in October my wife and I flew 11 hours to Bogotá.</p>
<p>We met a lot of new people and had a lot of fun with old friends as well. And while Colombia isn't on our list of favourite countries, it was great to have a major crypto event in a developing country for a change.</p>
<h2 id="findingbalance">⚖️ Finding Balance</h2>
<p>In my last year in review I talked about wanting to set better priorities and learning to say &quot;no&quot; to things that I would <em>love</em> to say &quot;yes&quot; to. Because we just can't do it all. And this is still something I'm struggling with.</p>
<p>I don't regret saying yes to building a startup even if it didn't work out. But there's one important lesson that I learned about priorities during my time working on Chaingrep, somewhat contrary to the ideas I started the year with. And that lesson is finding balance between priorities.</p>
<p>It's good to be focused on just one thing. But while working on Chaingrep I spent day and night just on building the startup. Only to throw away all that work and pivot to something completely different multiple times.</p>
<p>There will certainly be more times where I'll have to throw away work in the future. That is not necessarily a problem. But seeing how often you can fail and restart in a few months does show that you don't need to work day and night to make it work.</p>
<p>So going forward I want to be more balanced in the different aspects of my life. But as I mentioned before, it's generally hard for me to take it <em>too</em> easy. So I expect this to be an on-going challenge for me. We'll see how it goes.</p>
<h2 id="2023">📈 2023</h2>
<p>2022 was a rollercoaster. I got married and I am enjoying my new life together with my amazing wife. I also spent a lot of time trying to build a startup and subsequently crashing, but also restarting.</p>
<p>While Chaingrep fell, Revoke.cash flourished. The user base and community is there, but it's not consistently making enough money to sustain myself yet. So in 2023 I want to focus on getting revenue for Revoke.cash. But I want to work on it in a way that complements the rest of my life, rather than consuming it.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[🖼 2021 Year in Review: Bull Markets, NFTs & Evaluating Priorities]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>In my <a href="https://kalis.me/2020-year-in-review/">previous Year In Review</a> I mentioned we were heading into a new crypto bull market, and you can bet that we did. I got into crypto in the middle of the 2017 bull run, so this year was the first bull market I experienced from its onset.</p>
<p>It</p>]]></description><link>https://kalis.me/2021-year-in-review/</link><guid isPermaLink="false">60915830ba9ad5053dc6d32a</guid><category><![CDATA[life]]></category><category><![CDATA[year-review]]></category><dc:creator><![CDATA[Rosco Kalis]]></dc:creator><pubDate>Wed, 19 Jan 2022 23:02:53 GMT</pubDate><media:content url="https://kalis.me/content/images/2022/01/2021-year-in-review.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://kalis.me/content/images/2022/01/2021-year-in-review.jpg" alt="🖼 2021 Year in Review: Bull Markets, NFTs & Evaluating Priorities"><p>In my <a href="https://kalis.me/2020-year-in-review/">previous Year In Review</a> I mentioned we were heading into a new crypto bull market, and you can bet that we did. I got into crypto in the middle of the 2017 bull run, so this year was the first bull market I experienced from its onset.</p>
<p>It has mainly been exciting to see a lot of new people enter the space and to see many crypto projects gain legitimacy and mainstream attention. But at the same time it has been difficult to keep up with all the new developments and figure out what to spend my time on.</p>
<h2 id="cryptocurrency">⛓ Cryptocurrency</h2>
<p>Some DeFi tokens such as AAVE had already seen enormous growth in 2020 but many other projects also multiplied over tenfold in 2021. And though most coins have seen both ups and downs, the market as a whole has seen a lot of growth.</p>
<p>We saw the rise of NFTs in generative art and profile pictures and also a subsequent crash of many of these NFT projects. But while many projects are down from their highs it doesn't look like this bull market is over just yet.</p>
<h3 id="anyhedge">AnyHedge</h3>
<p>A big part of my year was spent working with <a href="https://generalprotocols.com/">General Protocols</a> on <a href="https://anyhedge.com/">AnyHedge</a>, the first DeFi protocol on Bitcoin Cash. This open-source protocol was first integrated into the non-custodial exchange Detoken around the new year, where it saw a volume of over $10m in just a few months.</p>
<p>This kind of volume is only a drop in the ocean compared to the kind of usage that ETH projects enjoy, but it was a good proof of concept to show that it's possible to do DeFi on UTXO-based chains such as BCH, albeit a very different kind.</p>
<p>In the summer Detoken shut down, which left AnyHedge without users. The rest of the year was spent improving the AnyHedge protocol, smart contracts and infrastructure, as well as building a new user-facing application that is set to release in 2022.</p>
<h3 id="cashscript">CashScript</h3>
<p>While working on AnyHedge, <a href="https://cashscript.org">CashScript</a> also received some major updates. Most importantly the entire language was overhauled for BCH's upcoming upgrade. The May 2022 network upgrade will bring multiplication and <em>native introspection</em>. When the upgrade hits, <em>covenant contracts</em>  will become much more accessible, flexible and efficient. And CashScript will support it from day 1.</p>
<h3 id="truffle">Truffle</h3>
<p>In my last <em>Year in Review</em> post I mentioned that I would be working with the <a href="https://trufflesuite.com/">Truffle</a> team on their upcoming Filecoin integration, which was launched at the end of March. After the successful <a href="https://consensys.net/blog/truffle-suite/truffle-and-ganache-now-filecoin-flavored/">Truffle + Filecoin launch</a>, I stuck around for another project that we dubbed the Truffle Dashboard.</p>
<img src="https://kalis.me/content/images/2022/01/truffle-dashboard-transaction.png" width="100%" alt="🖼 2021 Year in Review: Bull Markets, NFTs & Evaluating Priorities">
<p>Previously, deploying contracts with Truffle, Hardhat or similar tools required you to copy-paste a seed phrase to be used for the deployment. But as security guides will tell you, copy-pasting seed phrases is something you generally want to avoid.</p>
<p>So as an alternative, the Truffle Dashboard allows you to use your existing Metamask wallet when deploying smart contracts. I have been quietly working on the dashboard project and it is set to release early in 2022.</p>
<h3 id="brightunion">Bright Union</h3>
<p>Halfway into the year, a friend of mine reached out about <a href="https://brightunion.io/">Bright Union</a>, a project he started that offers aggregation of DeFi risk coverage. I joined the project as an advisor and I mainly help out with guidance on the Solidity side.</p>
<p>They had a successful product launch for their dashboard that allows you to compare different DeFi coverage products such as Nexus Mutual. They are currently working on expanding their product offering.</p>
<h2 id="nfts">🖼 NFTs</h2>
<p>The biggest hype this year was definitely NFTs, or Non-Fungible Tokens. While NFTs have been around for close to five years already, 2021 is the year they really picked up steam. Some older collections like <a href="https://larvalabs.com/cryptopunks">CryptoPunks</a> have seen their valuations increase over 50 times. And other wildly successful collections like <a href="https://boredapeyachtclub.com/">Bored Ape Yacht Club</a> and <a href="https://coolcatsnft.com/">Cool Cats</a> were launched and grew to massive valuations within this year.</p>
<h3 id="bastardganpunks">BASTARD GAN PUNKS</h3>
<p>The first NFT collection that I personally fell in love with was <a href="https://bastardganpunks.club/">BASTARD GAN PUNKS</a>. The <em>bastards</em> are derived from the attributes of the original CryptoPunks. These attributes are passed through a <a href="https://en.wikipedia.org/wiki/Generative_adversarial_network">GAN (Generative Adversarial Network)</a> to generate completely new images.</p>
<p>Because the images are generated by AI, some of them look very similar to the original CryptoPunks while others look completely messed up. This has created a collection with incredible diversity and a soul of its own.</p>
<h4 id="allbastardscom">ALLBASTARDS.COM</h4>
<p>When BASTARD GAN PUNKS launched in March, they had no attributes that they could be filtered on. NFT platforms like OpenSea don't lend themselves very well for collections like that, which made it very difficult to browse the collection.</p>
<img src="https://kalis.me/content/images/2022/01/allbastards.png" width="90%" alt="🖼 2021 Year in Review: Bull Markets, NFTs & Evaluating Priorities">
<p>As a solution I developed the <a href="https://allbastards.com">ALLBASTARDS.COM</a> gallery that displays the entire collection in a performant infinite scroll interface. As the community added more metadata attributes to filter on, I expanded the website to include those filters.</p>
<p>Towards the end of the year I started work on implementing a marketplace into ALLBASTARDS.COM, which has been a feature requested by many people within the community. By the end of the year I had gotten pretty far with the implementation, but I also had less and less time, so I asked help from the community.</p>
<img src="https://kalis.me/content/images/2022/01/allbastards-marketplace.png" width="90%" alt="🖼 2021 Year in Review: Bull Markets, NFTs & Evaluating Priorities">
<p>Since December fellow bastard <a href="https://github.com/christian-reynolds">Christian Reynolds</a> has been doing great work on additional features and final touches for the marketplace. Since he came on board I have mainly been offering guidance while he has been doing most of the development.</p>
<h3 id="revokecash">Revoke.cash</h3>
<p>As you may know, <a href="https://revoke.cash">revoke.cash</a> is a project that I started in 2019 that allows you to inspect and revoke all the ERC20 allowances that you've granted to DeFi protocols and other decentralised apps. I've written about this in the past (See <a href="https://kalis.me/unlimited-erc20-allowances/">Unlimited ERC20 allowances considered harmful</a>), but I hadn't written about how this applies to NFTs.</p>
<p>With the rise of NFTs we're seeing a lot of development at the intersection of NFTs and DeFi as well. Protocols like <a href="https://nftx.io">NFTX</a> or <a href="https://fractional.art">Fractional</a> provide interesting use cases at this intersection. But to use these kinds of applications (or even marketplaces), you need to grant an allowance similar to the one that exists for ERC20 tokens.</p>
<img src="https://kalis.me/content/images/2022/01/revoke-erc721.png" width="90%" alt="🖼 2021 Year in Review: Bull Markets, NFTs & Evaluating Priorities">
<p>As we've observed in the past, more hype also attracts more scams and exploits into the space. So with the hype that NFTs brought it was important to extend revoke.cash to enable inspecting and revoking of NFT allowances as well. I added these features in July and revoke.cash became the first platform for NFT allowances.</p>
<h3 id="generativeart">Generative art</h3>
<p>One big theme of NFTs this year was generative art, meaning that the artist writes an algorithm that is used to produce artworks. This combination of the artist's vision, the algorithm and the aspect of randomness can make for incredibly interesting artworks.</p>
<p>Some famous collections that were created last year are Tyler Hobbs' Fidenza and Dmitry Cherniak's Ringers, but there are hundreds of beautiful collections with hundreds or thousands of artworks per collection.</p>
<p>Seeing the beauty that these algorithms could generate I decided to dive into it myself as well. So in my spare time I have been teaching myself generative art, both through trial-and-error as well as a plethora of online resources, of which <a href="https://tylerxhobbs.com/essays">Tyler Hobbs' blog</a> has definitely been one of the most useful.</p>
<h4 id="imperfectionsbykalis">Imperfections by Kalis</h4>
<p>My father has been an artist for most of his life, so as I got into generative art I decided it would be a great opportunity to combine our skills and start an art project together. At first he didn't really understand what all these <em>NFTs</em> and <em>generative art</em> were about, but after showing him some existing works he was 100% convinced.</p>
<img src="https://kalis.me/content/images/2022/01/imperfections-by-kalis.jpg" width="90%" alt="🖼 2021 Year in Review: Bull Markets, NFTs & Evaluating Priorities">
<p>Much of my father's physical work is based around the imperfections that come with the manual execution of otherwise perfect shapes - in his case, stripes. Converting that philosophy from the physical world to a generative digital artwork is a challenge, since the execution of a computer is inherently perfect.</p>
<p>We're both very happy with the progress we've made on the project and the results of the algorithm as it is now. It has also been a great experience to collaborate with my father on a topic like this, since generative digital art is directly at the intersection of our two worlds - art and code.</p>
<h2 id="events">🎙 Events</h2>
<p>As the world got used to COVID we slowly saw more in-person events popping up. We're definitely not back to pre-COVID levels, but there were some great gatherings this year and it looks like the number of events will be ramped up further in 2022.</p>
<h3 id="nfthackhackmoney">NFTHack &amp; HackMoney</h3>
<p>The first events I attended were still digital. And while I've expressed that I don't particularly enjoy these types of events, I decided to give them another go. I enjoyed working on <em>Radical Domains</em> in 2020 and NFTHack presented itself as a way to work more on harberger taxes and radical markets.</p>
<p>With the new <em>Radical Protocol</em> we set out to generalise the concept of Radical Domains, but <strong>for any NFT</strong>. Applying this to existing NFTs proved harder than we initially thought, so instead we built a platform that could be used by creators to mint new NFTs under a Harberger system.</p>
<p>This idea landed us a grant from the <a href="https://rarible.org/">Rarible DAO</a>, which allowed us to expand this system and we used the subsequent HackMoney hackathon to further the project. While the progress was promising, we had to give up due to lack of time and resources.</p>
<h3 id="ethcc">EthCC</h3>
<p>Come summer, it looked like it was finally time to leave the virtual events behind, as the European Ethereum community gathered in Paris for the fourth annual Ethereum Community Conference. Interesting to note is that EthCC was also the last major conference that wasn't cancelled in 2020.</p>
<img src="https://kalis.me/content/images/2021/08/ethcc-4.jpg" width="90%" alt="🖼 2021 Year in Review: Bull Markets, NFTs & Evaluating Priorities">
<p>At EthCC I gave a presentation about the potential dangers of the <em>unlimited allowance</em> pattern that is often used in modern DeFi applications. This is a topic that I wrote about in 2020 and I had been meaning to do a conference talk about for a while now.</p>
<p>Overall it was great to reconnect with a bunch of people that I hadn't seen in years and to connect with some people that got into the space recently as well. But although travel had mostly opened up by that time, most attendees were still EU-based.</p>
<h3 id="ethlisbon">ETHLisbon</h3>
<p>A couple of months after EthCC travelling became even more widespread and the next big event was the Lisbon Blockchain week, championed by Liscon and ETHLisbon. I wasn't planning to go, but in the end the Truffle team managed to convince me to come over even though I had no tickets to any of the events.</p>
<img src="https://kalis.me/content/images/2022/01/ethlisbon-bastards.jpg" width="90%" alt="🖼 2021 Year in Review: Bull Markets, NFTs & Evaluating Priorities">
<p>The absence of tickets proved not to be a problem at all as my days were still packed with activities. I had a lot of people to catch up with and a lot of board games to play with the Truffle team back at the hotel, so in the end it was a great success.</p>
<p>A more unfortunate result of the jam-packed weekend of events and meetups was that I tested positive for COVID upon returning home. So what started as a fun break ended up taking quite some time to recover from. It was a great weekend though!</p>
<h2 id="opensource">🐙 Open Source</h2>
<p>2021 has been a good year for my open source projects. truffle-plugin-verify has ended the year with almost 5x the monthly downloads and revoke.cash ended the year with more than 10x the monthly users.</p>
<img src="https://kalis.me/content/images/2022/01/npm-downloads.png" width="90%" alt="🖼 2021 Year in Review: Bull Markets, NFTs & Evaluating Priorities">
<p>truffle-plugin-verify and revoke.cash doubled their GitHub Stars while my other projects also saw some growth in stars. My NPM packages now get a combined 35k+ monthly downloads, mostly because of the tremendous growth of truffle-plugin-verify.</p>
<img src="https://kalis.me/content/images/2021/12/star-history.png" width="90%" alt="🖼 2021 Year in Review: Bull Markets, NFTs & Evaluating Priorities">
<p>A big topic in my open source projects was multi-chain support. 2021 gave rise to a lot of <em>alternative</em> smart contract chains, such as Polygon and Avalanche. truffle-plugin-verify supports most of these alternative chains, while revoke.cash supports a few. It does look like these chains are here to stay so multi-chain support will likely need to be expanded in years to come.</p>
<p>As always, the power of open source is having outside contributors. And like previous years I've had some great help building my open source projects. For revoke.cash I want to thank <a href="https://github.com/dawsbot">Dawson Botsford</a>, <a href="https://github.com/merwane">Merwane Drai</a>, <a href="https://github.com/danielheyman">Daniel Heyman</a>, <a href="https://github.com/timjrobinson">Tim Robinson</a> and <a href="https://github.com/andreujuanc">Juan Andreu</a> for their contributions.</p>
<p>For CashScript I want to thank <a href="https://github.com/blockparty-sh">Jt Freeman</a>, <a href="https://github.com/Sydwell">Sydwell</a> and <a href="https://github.com/mr-zwets">mr-zwets</a> for their fixes. But most of all I want to highlight <a href="https://github.com/nathanielCherian">Nathaniel Cherian</a>, who implemented several very important features, including tuple assignment and date literals - all while still being in high school!</p>
<p>For truffle-plugin-verify I want to thank <a href="https://github.com/oscarmartj">oscarmartj</a>, <a href="https://github.com/TimonPeng">Timon Peng</a>, <a href="https://github.com/JasoonS">Jason Smythe</a>, <a href="https://github.com/fredlacs">fredlacs</a>, <a href="https://github.com/nhancv">Nhan Cao</a>, <a href="https://github.com/albertov19">albertov19</a> and <a href="https://github.com/ChaO-0">Christopher Yu</a> for expanding multi-chain support. I also want to thank <a href="https://github.com/hellwolf">Miao ZhiCheng</a> and <a href="https://github.com/keyboard-clacker">Kyle Holzinger</a> for expanding and improving the CLI.</p>
<h2 id="focuspriorities">📊 Focus &amp; Priorities</h2>
<p>One side effect of the bull market is that there's a lot more going on in the space than there was in the years prior. This means that it's inevitably unattainable to keep up with all the development - however interesting they may be. If you combine this with the overflow of new projects that are looking for contributors, it's impossible to get involved with every project - however interesting they may be.</p>
<img src="https://kalis.me/content/images/2022/01/saying-no.png" width="90%" alt="🖼 2021 Year in Review: Bull Markets, NFTs & Evaluating Priorities">
<p>I recently came across this quote from <a href="https://twitter.com/levelsio">Pieter Levels</a>: <em>The only way to get what you want in life is to repeatedly say no to what you don't</em>. And this is something I want to internalise. Even though there are a lot of things that are interesting, that doesn't mean I need to be involved with all of them.</p>
<p>I have always done many things at the same time, and it has always seemed counter-intuitive to say <em>no</em> to something that I find interesting. But in the end I only have a limited amount of time, which means that I need to be much more selective with it. And besides saying <em>no</em> to new things, this also meant re-evaluating the things I was already spending my time on.</p>
<img src="https://kalis.me/content/images/2022/01/quit-stoopingams.jpg" width="100%" alt="🖼 2021 Year in Review: Bull Markets, NFTs & Evaluating Priorities">
<p>The first casualty of this re-evaluation was the <em>@stoopingams</em> instagram account. While I had only started it a year earlier, the account grew to be pretty popular and it needed attention multiple times a day. Luckily I was able to find a replacement from within the <em>@stoopingams</em> community so that the account can live on.</p>
<p>The second thing I found is that I don't want to be spending a large portion of my time on Bitcoin Cash. I've been involved in BCH since I created CashScript for my Master's thesis back in 2019. And while I spent a a lot of time on BCH, I was also active in other cryptocurrency communities like Ethereum. And in the end I'm just a lot more excited about Ethereum than I am about other projects.</p>
<p>Finally, I have been involved with a bunch of my own side projects, as you probably know if you're reading this blog. I started these projects for fun as I enjoy coding and building as a hobby. Even when I need a break from programming for work, I can keep programming on my side projects.</p>
<p>But as some of these projects grew, they sometimes started feeling more like a job than a hobby. Especially for truffle-plugin-verify I've found myself debugging other people's issues time and again, which has gotten tiring.</p>
<p>So I've become much more selective on the time I'm spending on my side projects as well. I've set up much stricter issue templates and I try to be very explicit about any expectations that users may have about future development (they shouldn't have any).</p>
<h3 id="armada">Armada</h3>
<p>So come the end of 2021 I've ended my BCH work with General Protocols, I've finished my contracting engagement with Truffle, I'm only working on my personal projects for enjoyment, and I've let go of some time intensive activities like @stoopingams.</p>
<p>So by saying <em>no</em> to all those things, the remaining question is what I am saying <em>yes</em> to. The answer is <a href="https://tryarmada.com/">Armada</a>, which is the startup that I am currently working on with my friend <a href="https://twitter.com/merwanedr">Merwane Drai</a>. Together we're tackling the problem of NFT discoverability by building a search engine for NFTs.</p>
<p>That is all I can share about Armada for now, but stay tuned for an exciting 2022. We have a bunch of great things in store that we can hopefully share soon.</p>
<h2 id="2022">📈 2022</h2>
<p>In 2021 I helped build important new products for Bitcoin Cash and Ethereum. I got started with collecting NFTs and creating generative art. I added significant new features to my open source tools and apps which have seen tremendous growth.</p>
<p>While I have started to spend less time on my open source side projects, I do want to make sure they stay relevant. I hope to attract more community contributors to get involved with these projects so that I can shift my more time towards guiding them instead of developing myself.</p>
<p>In 2021 I had to step back and re-evaluate what I wanted to do, but by the end of the year I was able to set my priorities straight. I plan on going into 2022 with more focus and I am excited to build Armada together with Merwane.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[The best peer-to-peer NFT trading platforms in 2021]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Since the start of 2021, Non-Fungible Tokens (NFTs) have become increasingly popular inside and outside of existing crypto communities. Trading volumes for NFTs have skyrocketed and a bunch of new communities have formed around them.</p>
<p>Most NFTs are traded on marketplaces such as <a href="https://opensea.io/?ref=0xe126b3e5d052f1f575828f61feba4f4f2603652a">OpenSea</a> or <a href="https://rarible.com">Rarible</a>. The main value of</p>]]></description><link>https://kalis.me/best-nft-trading-peer-to-peer-2021/</link><guid isPermaLink="false">613354ceba9ad5053dc6d3a0</guid><category><![CDATA[ethereum]]></category><category><![CDATA[nft]]></category><category><![CDATA[crypto]]></category><dc:creator><![CDATA[Rosco Kalis]]></dc:creator><pubDate>Thu, 02 Dec 2021 21:43:07 GMT</pubDate><media:content url="https://kalis.me/content/images/2021/11/P2P-NFT-Trading_with-logotype-bigger-2.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://kalis.me/content/images/2021/11/P2P-NFT-Trading_with-logotype-bigger-2.png" alt="The best peer-to-peer NFT trading platforms in 2021"><p>Since the start of 2021, Non-Fungible Tokens (NFTs) have become increasingly popular inside and outside of existing crypto communities. Trading volumes for NFTs have skyrocketed and a bunch of new communities have formed around them.</p>
<p>Most NFTs are traded on marketplaces such as <a href="https://opensea.io/?ref=0xe126b3e5d052f1f575828f61feba4f4f2603652a">OpenSea</a> or <a href="https://rarible.com">Rarible</a>. The main value of these marketplaces lies in their big orderbooks of listings and bids to choose from. This allows anyone to buy and sell NFTs from anyone else without needing to <em>actually</em> interact with their counterparty.</p>
<p>This works great when you want to buy and sell your NFTs for ETH or other cryptocurrencies. <strong>But it is lacking when you want to trade NFTs for other NFTs</strong> or when you want to negotiate a deal with your counterparty.</p>
<p>So today we're looking into platforms that are specifically tailored for trading NFTs in a peer-to-peer (P2P) manner rather than using a centralised marketplace.</p>
<h2 id="whatplatformsexistin2021">What platforms exist in 2021?</h2>
<p>Three popular P2P trading platforms are <a href="https://www.nfttrader.io/">NFTTrader</a>, <a href="https://otc.sudoswap.xyz/">Sudoswap</a> and <a href="https://swap.kiwi/">Swap.kiwi</a>. All three were created in this past year so the ecosystem is still young and we should expect more platforms to be developed in the future.</p>
<h3 id="nfttrader">NFTTrader</h3>
<img src="https://kalis.me/content/images/2021/11/nfttrader.png" width="90%" alt="The best peer-to-peer NFT trading platforms in 2021">
<p>NFTTrader was the first P2P NFT trading platform as it was launched in January of 2021 by a team of <a href="https://www.nfttrader.io/team">four founders</a>. The team created their own custom asset-swap smart contracts that allows users to swap any combination of ERC20, ERC721 and ERC1155 tokens, as well as ETH.</p>
<h3 id="sudoswap">Sudoswap</h3>
<img src="https://kalis.me/content/images/2021/11/sudoswap.png" width="90%" alt="The best peer-to-peer NFT trading platforms in 2021">
<p>Sudoswap was created by well-known developer <a href="https://twitter.com/0xmons">0xmons</a> in April of 2021. And while NFTTrader created their own smart contracts, Sudoswap uses the open-source <a href="https://0x.org/">0x</a> protocol to create and settle swaps between any combination of ERC20, ERC721 and ERC1155 tokens.</p>
<h3 id="swapkiwi">Swap.kiwi</h3>
<img src="https://kalis.me/content/images/2021/11/swapkiwi.png" width="90%" alt="The best peer-to-peer NFT trading platforms in 2021">
<p>Swap.kiwi is the youngest platform of the bunch as it was created in June of 2021 by ape punk <a href="https://twitter.com/niftynaut">niftynaut</a>. They developed their own smart contracts to execute swaps between any combination of ERC721 and ETH.</p>
<h2 id="howdotheplatformscompare">How do the platforms compare?</h2>
<p>To compare the different platforms, I asked <a href="https://twitter.com/snooplyin42">snooplyin42</a> to help out. Together we sat down and swapped 2 for 2 <a href="https://bastardganpunks.club/">BASTARD GAN PUNKS</a> NFTs on each of the platforms and discussed the pros and cons of each platform.</p>
<h3 id="featuresfunctionality">Features &amp; functionality</h3>
<p>In its essence these three platforms offer the same basic functionality. Namely, they allow you to swap baskets of NFTs and fungible tokens with a counterparty. But the supported assets are slightly different.</p>
<p>NFTTrader has the broadest asset support as it allows baskets of any ERC20, ERC721, ERC1155 or ETH tokens. Sudoswap also supports ERC20, ERC721 and ERC1155, but lacks support for the native ETH token (although WETH can be used). Swap.kiwi has the smallest list of supported assets since it <em>only</em> supports ERC721 and ETH.</p>
<p>Sudoswap boasts two additional features that the other two platforms lack. First, it allows you to leave the counterparty blank, which means that <em>anyone</em> can fill the order. This is particularly useful when trading your NFTs for fungible tokens.</p>
<p>Second, it allows you to set an expiry date/time. When this date/time is reached, the proposed swap automatically becomes invalid. This gives you the option to let a swap expire rather than having to send an on-chain transaction to cancel the swap. This limits the downside when the counterparty decides not to go through with a swap.</p>
<p>However, one important feature limitation of Sudoswap is that it's impossible to create swaps from multisig wallets or smart contract wallets like Argent. NFTTrader and Swap.kiwi do fully support multisig wallets (and smart contract wallets).</p>
<table>
<thead>
<tr>
<th></th>
<th style="text-align:right">Sudoswap</th>
<th style="text-align:right">NFTTrader</th>
<th style="text-align:right">Swap.kiwi</th>
</tr>
</thead>
<tbody>
<tr>
<td>ETH</td>
<td style="text-align:right">❌</td>
<td style="text-align:right">✅</td>
<td style="text-align:right">✅</td>
</tr>
<tr>
<td>ERC20</td>
<td style="text-align:right">✅</td>
<td style="text-align:right">✅</td>
<td style="text-align:right">❌</td>
</tr>
<tr>
<td>ER721</td>
<td style="text-align:right">✅</td>
<td style="text-align:right">✅</td>
<td style="text-align:right">✅</td>
</tr>
<tr>
<td>ERC1155</td>
<td style="text-align:right">✅</td>
<td style="text-align:right">✅</td>
<td style="text-align:right">❌</td>
</tr>
<tr>
<td>Blank counterparty</td>
<td style="text-align:right">✅</td>
<td style="text-align:right">❌</td>
<td style="text-align:right">❌</td>
</tr>
<tr>
<td>Expiry date/time</td>
<td style="text-align:right">✅</td>
<td style="text-align:right">❌</td>
<td style="text-align:right">❌</td>
</tr>
<tr>
<td>Multisig support</td>
<td style="text-align:right">❌</td>
<td style="text-align:right">✅</td>
<td style="text-align:right">✅</td>
</tr>
</tbody>
</table>
<h3 id="overallwebsiteui">Overall website &amp; UI</h3>
<p>The Sudoswap UI has a sort of vapourwave windows 95 look. There are plenty of people - especially within the crypto space - that enjoy this kind of aesthetic. At the same time it can also be a barrier to entry for people more used to a regular &quot;web2&quot; aesthetic. But while the aesthetic may be an acquired taste, the functionality is laid out clearly and it's easy to find what you're looking for.</p>
<p>Swap.kiwi, on the other hand, has a very clean and much more approachable UI than the other two. It is laser-focused on <em>only</em> offering the swap functionality in a single page, without any secondary features. This makes the app very accessible and familiar, especially for web2 users. At the same time, it could be good to offer more information or explanation about the app itself.</p>
<p>While the UIs of Sudoswap and Swap.kiwi are mainly centred around the swap features, NFTTrader has a lot more going on. On one hand, this means they include a lot more useful information and explanations. But it also means that <strong>it includes some less relevant features</strong>, such as leaderboards, gamification and their own NFTs.</p>
<p>All platforms have some small UI bugs and weird quirks that still need to be polished out. As an example, each of the platforms seemed to have <em>some</em> trouble with updating the UI after certain blockchain actions were executed (e.g. approval or accept swap). But that is a notorious problem in blockchain development in general.</p>
<p>Among the platforms, we ran into the most UI quirks with Sudoswap, while NFTTrader had the most polished experience, albeit still not without quirks. This makes sense as Sudoswap was recently overhauled while NFTTrader has been able to mature since its January launch.</p>
<h3 id="swapflowux">Swap flow &amp; UX</h3>
<p>While all three platforms achieve similar things, the swap process and UX is drastically different among the platforms.</p>
<h4 id="sudoswap">Sudoswap</h4>
<p>On Sudoswap you can propose a new swap by choosing the &quot;create swap&quot; option. You can then select the tokens they are putting up for the swap and the tokens they want to get in return.</p>
<p>When selecting which tokens to send, it automatically displays the NFTs that you have in your wallet, but any kind of search functionality is lacking here, so scrolling through a large wallet can get very tedious.</p>
<p>If you can't find the tokens you're looking for, you can use the included &quot;form&quot; to add other tokens. The form requires you to copy-paste a contract address + token ID, so <strong>this process is only really suited to advanced users</strong>.</p>
<img src="https://kalis.me/content/images/2021/11/sudoswap-select-nfts.png" width="90%" alt="The best peer-to-peer NFT trading platforms in 2021">
<p>When selecting which NFTs to receive, you can choose from a default list of NFT collections, where you have to specify which token ID you want to receive. While some of the bigger collections are included, <strong>some very notable collections are missing</strong>, such as Cool Cats or CrypToadz. For these contracts you have to use the earlier mentioned form, which again requires you to look up and paste the token's contract address, which is advanced functionality.</p>
<img src="https://kalis.me/content/images/2021/11/sudoswap-select-counterparty-nfts.png" width="90%" alt="The best peer-to-peer NFT trading platforms in 2021">
<p>Once you're happy with your swap, you have to send an approval transaction for every separate collection and you can optionally specify values for the counterparty address and expiry date, and finally click &quot;create swap&quot;. From there you are prompted to sign a message through your wallet, after which a swap link is created which can be shared with your counterparty.</p>
<img src="https://kalis.me/content/images/2021/11/sudoswap-swap.png" width="90%" alt="The best peer-to-peer NFT trading platforms in 2021">
<p>If you specified a counterparty address, then they can also find it under the &quot;your swaps&quot; section of the website. After sending the required approval transactions they can accept the swap with a transaction.</p>
<h4 id="nfttrader">NFTTrader</h4>
<p>Similar to Sudoswap, NFTTrader allows you to specify a list of tokens you want to send and a list of tokens you want to receive. Unlike Sudoswap, NFTTrader has a more mature selection interface with an easy-to-use search functionality.</p>
<img src="https://kalis.me/content/images/2021/11/nfttrader-select-nfts.png" width="90%" alt="The best peer-to-peer NFT trading platforms in 2021">
<p>But while Sudoswap allows you to add custom tokens through the &quot;form&quot;, NFTTrader is limited to the tokens that are enabled in the UI. If your token is not in the list you'll need to contact the NFTTrader team to have it added. Luckily the team is responsive in doing so.</p>
<p>Also unlike Sudoswap, with NFTTrader you <em>have</em> to specify a counterparty address. But the great thing about that is that you can then select the NFTs you want to receive by browsing your counterparty's wallet, rather than having to copy-paste token IDs.</p>
<img src="https://kalis.me/content/images/2021/11/nfttrader-select-counterparty-nfts.png" width="90%" alt="The best peer-to-peer NFT trading platforms in 2021">
<p>Once you're happy with your swap, you have to send an approval transaction for every separate collection, after which you click &quot;pay now&quot;. This prompts you to send a transaction, after which a swap link is created.</p>
<img src="https://kalis.me/content/images/2021/11/nfttrader-swap.png" width="90%" alt="The best peer-to-peer NFT trading platforms in 2021">
<p>Similar to Sudoswap, you can either send that swap link to your counterparty or they can find it under their &quot;my swaps&quot; section. After sending the required approval transactions the counterparty can accept the swap with a final transaction.</p>
<h4 id="swapkiwi">Swap.kiwi</h4>
<p>Of the three platforms, Swap.kiwi definitely has the most unorthodox UX. Rather than proposing a full swap you can only select the NFTs that you're putting up for the trade as well as the counterparty's address. After approving the assets they then send an initial transaction to start the trade.</p>
<img src="https://kalis.me/content/images/2021/11/swapkiwi-select-nfts.png" width="90%" alt="The best peer-to-peer NFT trading platforms in 2021">
<p>The counterparty can then see this &quot;half swap&quot; under their &quot;open swaps&quot; section, where they can add the NFTs that they are putting up. After approving those assets they can create a <em>full</em> swap by sending another transaction.</p>
<img src="https://kalis.me/content/images/2021/11/swapkiwi-select-counterparty-nfts.png" width="90%" alt="The best peer-to-peer NFT trading platforms in 2021">
<p>The initial swap creator then has to find the swap under &quot;open swaps&quot; and after inspecting the swap terms, they can finally accept the swap with a final transaction. In total this flow requires three transactions, and both parties have to select the NFTs they're putting up for the trade.</p>
<p>The good thing about this kind of flow is that both parties have full control over the assets they select for the swap, which makes scamming your counterparty slightly more difficult. But the problem with it is that <strong>it's fundamentally not how people think about P2P swaps</strong>.</p>
<p>Because the maker and the taker are usually in contact through some other means to negotiate a fair trade <em>before</em> settling it on-chain, the maker can propose the <em>full</em> trade, rather than having these on-chain &quot;negotiation&quot; steps.</p>
<h3 id="gascostfees">Gas cost &amp; fees</h3>
<p>When looking at gas efficiency and fees, <strong>Sudoswap comes out miles ahead</strong>. Because the platform uses well-established and gas-efficient contracts (through 0x) and they only require a <em>single</em> transaction, the total gas required for the swap is around a third of that of the other two platforms. And for the maker, Sudoswap doesn't cost any gas at all.</p>
<p>Additionally, Sudoswap is the only platform to include an expiry date for a swap, meaning that it's possible to let a swap expire rather than cancelling it through an on-chain transaction. The other two platforms do not have that functionality, so if you want to cancel a swap, you'll incur additional transaction fees.</p>
<p>Finally, both NFTTrader and Swap.kiwi include a flat platform fee to both the maker and the taker, while Sudoswap charges no fees at all.</p>
<p>Below is a comparison of the total fees paid for a 2-for-2 NFT swap at different gas price levels. As can be seen, the difference between NFTTrader and Swap.kiwi is not as significant, although NFTTrader is slightly more expensive - despite Swap.kiwi needing three transactions.</p>
<p>But the difference between Sudoswap and the others is immense, as it is <strong>at least three times cheaper</strong> at every gas level, while it's even six times cheaper than NFTTrader when gas is at 10 gwei due to the platform fees.</p>
<table>
<thead>
<tr>
<th></th>
<th style="text-align:right">Sudoswap</th>
<th style="text-align:right">NFTTrader</th>
<th style="text-align:right">Swap.kiwi</th>
</tr>
</thead>
<tbody>
<tr>
<td>Maker gas used</td>
<td style="text-align:right">0</td>
<td style="text-align:right">810k</td>
<td style="text-align:right">740k</td>
</tr>
<tr>
<td>Taker gas used</td>
<td style="text-align:right">370k</td>
<td style="text-align:right">440k</td>
<td style="text-align:right">330k</td>
</tr>
<tr>
<td>Maker platform fees</td>
<td style="text-align:right">0 ETH</td>
<td style="text-align:right">0.005 ETH</td>
<td style="text-align:right">0.0025 ETH</td>
</tr>
<tr>
<td>Taker platform fees</td>
<td style="text-align:right">0 ETH</td>
<td style="text-align:right">0.005 ETH</td>
<td style="text-align:right">0.0025 ETH</td>
</tr>
<tr>
<td>Total cost (gas = 10 gwei)</td>
<td style="text-align:right">0.0037 ETH</td>
<td style="text-align:right">0.0225 ETH</td>
<td style="text-align:right">0.0157 ETH</td>
</tr>
<tr>
<td>Total cost (gas = 100 gwei)</td>
<td style="text-align:right">0.037 ETH</td>
<td style="text-align:right">0.135 ETH</td>
<td style="text-align:right">0.112 ETH</td>
</tr>
<tr>
<td>Total cost (gas = 1000 gwei)</td>
<td style="text-align:right">0.37 ETH</td>
<td style="text-align:right">1.26 ETH</td>
<td style="text-align:right">1.075 ETH</td>
</tr>
</tbody>
</table>
<h3 id="popularityusage">Popularity &amp; usage</h3>
<p>To measure popularity of each of the platforms we look at the number of trades in the past 30 days (as of 30 Nov 2021). In doing so it's important to make a distinction between <em>swaps</em>, where both sides include NFTs, and <em>sales</em>, where only a single side includes NFTs. Note that Swap.kiwi only supports <em>swaps</em>.</p>
<p>To get this data I put together a <a href="https://github.com/rkalis/nft-swaps-data">script</a> that retrieves the transaction data from the blockchain and classifies the different trades as either a swap or a sale. Another important metric is the <em>value</em> of those trades. So besides the classification of trades, the script also estimates their value. Note that <em>both</em> sides of the trade are included in the total value.</p>
<p>For ETH and ERC20 tokens it uses the latest USD price, while for NFTs it calculates the recent average sale price. <strong>This estimation is not perfect</strong> since the crypto and NFT markets are very volatile, so the <em>current</em> price may not accurately reflect the price at the time of the trade. Additionally, the estimate may not accurately reflect the price of 1/1 NFTs or shared collections like Art Blocks or Foundation.</p>
<p>As can be seen in the table below, NFTTrader is leading in number of monthly swaps and sales, with Sudoswap having about half the sales and 2/3rd the swaps that NFTTrader has. Swap.kiwi on the other hand doesn't come close to the other two, as it only has less than 20% of NFTTrader's monthly swaps.</p>
<p>But when looking at the value of the trades, we see a different story. While Swap.kiwi has a much smaller number of swaps, the average value is more than twice as high. This seems to indicate that Swap.kiwi is the preferred platform for higher value trades.</p>
<p>Another interesting observation is the difference between swaps and sales. The total number of sales across the platforms is more than twice the total number of swaps and the total dollar volume of sales is almost five times the volume of swaps, showing that <strong>sales are still the main use case for these platforms</strong>.</p>
<table>
<thead>
<tr>
<th>volume(30d)</th>
<th style="text-align:right">Sudoswap</th>
<th style="text-align:right">NFTTrader</th>
<th style="text-align:right">Swap.kiwi</th>
</tr>
</thead>
<tbody>
<tr>
<td>Number of swaps</td>
<td style="text-align:right">209</td>
<td style="text-align:right">281</td>
<td style="text-align:right">42</td>
</tr>
<tr>
<td>Swap volume</td>
<td style="text-align:right">$3.3m</td>
<td style="text-align:right">$8.1m</td>
<td style="text-align:right">$2.6m</td>
</tr>
<tr>
<td>Average swap value</td>
<td style="text-align:right">$16k</td>
<td style="text-align:right">$29k</td>
<td style="text-align:right">$61k</td>
</tr>
<tr>
<td>Number of sales</td>
<td style="text-align:right">349</td>
<td style="text-align:right">788</td>
<td style="text-align:right">N/A</td>
</tr>
<tr>
<td>Sale volume</td>
<td style="text-align:right">$17.1m</td>
<td style="text-align:right">$55.8m</td>
<td style="text-align:right">N/A</td>
</tr>
<tr>
<td>Average sale value</td>
<td style="text-align:right">$49k</td>
<td style="text-align:right">$71k</td>
<td style="text-align:right">N/A</td>
</tr>
</tbody>
</table>
<h3 id="smartcontractsaudits">Smart contracts &amp; audits</h3>
<p>NFTTrader and Swap.kiwi developed their own contracts, while Sudoswap uses the open source 0x contracts. The 0x contracts were audited by ConsenSys Diligence, Swap.kiwi was audited by Hacken.io, and NFTTrader has received review from Carl Farterson. All contracts are available on Etherscan: <a href="https://etherscan.io/address/0xc310e760778ecbca4c65b6c559874757a4c4ece0#code">NFTTrader</a>, <a href="https://etherscan.io/address/0x080bf510fcbf18b91105470639e9561022937712#code">0x</a>, <a href="https://etherscan.io/address/0x4748495153fb86637e4fdd8e50e3c1f611f15930#code">Swap.kiwi</a>. The audits are also available online: <a href="https://github.com/best-practicers/reviews/tree/main/reviews/NFTTrader">NFTTrader</a>, <a href="https://github.com/ConsenSys/0x_audit_report_2018-07-23">0x</a>, <a href="https://swapkiwi.medium.com/swap-kiwi-audit-result-8e83270247af">Swap.kiwi</a>.</p>
<h2 id="verdict">Verdict</h2>
<p>All three platforms are designed to trade baskets of assets in a peer-to-peer fashion, but Sudoswap and NFTTrader offer a more diverse set of assets than Swap.kiwi. In addition, Sudoswap has some interesting extra swap features that do not exist on the other two platforms. However, Sudoswap does not support multisig wallets.</p>
<p>The swap UX of Sudoswap and NFTTrader is <em>very</em> similar. The main differences are in NFTTrader having a more polished and accessible experience and Sudoswap only requiring a single transaction. Swap.kiwi employs a very different swap UX that requires a total of three transactions.</p>
<p>Despite requiring three transactions, Swap.kiwi comes out slightly ahead of NFTTrader in terms of gas and platform fees, but neither is a match for the gas efficiency of Sudoswap, which is at least three times cheaper to use than the other platforms.</p>
<p>NFTTrader is the most mature product of the bunch and has the most active users, but Sudoswap is gaining fast after their overhaul in October. Swap.kiwi is lagging behind in terms of usage, but is also the youngest of the three platforms.</p>
<p>And while NFTTrader is a more polished product, Sudoswap comes out as the clear winner in this comparison due to its added functionality and vastly superior cost effectiveness.</p>
<hr>
<p>Cover art by <a href="https://twitter.com/Visual_vibes__">Kajal Baliyan</a></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[🏠 2020 Year in Review: Staying Home, Vintage Furniture & DeFi]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>2020 was a weird year. COVID changed the dynamics of life around the world. Governments enforced lockdowns and business had to close. Stock markets crashed, only to bounce back spectacularly. Remote working became the norm, while travel completely shut down.</p>
<p>Much of 2020 was about figuring out how to live</p>]]></description><link>https://kalis.me/2020-year-in-review/</link><guid isPermaLink="false">5e99c377c28b3503c573753d</guid><category><![CDATA[life]]></category><category><![CDATA[year-review]]></category><dc:creator><![CDATA[Rosco Kalis]]></dc:creator><pubDate>Tue, 19 Jan 2021 13:28:55 GMT</pubDate><media:content url="https://kalis.me/content/images/2021/01/staying-home.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://kalis.me/content/images/2021/01/staying-home.jpg" alt="🏠 2020 Year in Review: Staying Home, Vintage Furniture & DeFi"><p>2020 was a weird year. COVID changed the dynamics of life around the world. Governments enforced lockdowns and business had to close. Stock markets crashed, only to bounce back spectacularly. Remote working became the norm, while travel completely shut down.</p>
<p>Much of 2020 was about figuring out how to live with these new dynamics. Working from home, eating in, learning to have fun without going out. When the weather was nice, meeting friends in the park was a good alternative, but in winter, that doesn't sound very appealing.</p>
<p>Despite everything going on in the world, I feel like my year was pretty good. I didn't travel as much as I would have liked, but with a beautiful house by the canals, I did get the unique experience of Amsterdam without any tourists.</p>
<h2 id="writing">✍️ Writing</h2>
<p>Around March and April I had a bit more time on my hands due to COVID. So I used that extra time to catch up on a few posts that I still wanted to write. Most notably I finished my Year in Review for 2019 - about 3 months late.</p>
<p>Around that time I also revisited some old love2d gamedev projects, where I spent a lot of time debugging and automating the web build of my game. This led me to publish a guide on <a href="https://kalis.me/building-love2d-games-web-docker/">building love2d games for the browser</a> so that others in the same situation hopefully will have an easier time.</p>
<p>While I had several articles in draft for a while, during the rest of the year the only other article I was able to finish was <a href="https://kalis.me/unlimited-erc20-allowances">Unlimited ERC20 allowances considered harmful</a>. This was an article that I wanted to write since the end of 2019 when I initially created <a href="https://revoke.cash">revoke.cash</a>, but it took me a long time of on-and-off writing to finish it.</p>
<p>So I didn't write a lot this year, but I am fine with that. There are plenty of other ways I am sharing my knowledge, such as conferences, open source code, or <a href="https://ethereum.stackexchange.com/users/38376/rosco-kalis">StackExchange</a>. And in comparison writing a high quality article takes up much more time. So it is good to be selective with what I write and only publish something when it really adds to the conversation.</p>
<h2 id="stayinghome">🏠 Staying Home</h2>
<p>In my <a href="https://kalis.me/2019-year-in-review">2019 Year in Review</a> I had a section dedicated to travelling as I had flown around the world twice that year. So I figured with much of the world in lockdown, it'd make sense to have a similar section titled <em>Staying Home</em> this time around.</p>
<p>My girlfriend and I returned to Amsterdam around the end of 2019. We had some trouble adjusting to the &quot;regular&quot; life back home, as well as the cold winter weather. But after a while we started settling in and we found a new rhythm here.</p>
<p>We were still renting our old student apartment, so we quickly had to start looking for a new home (student leases automatically end six months after graduation). The house search was challenging, but we ended up with a beautiful apartment on Amsterdam's innermost canal.</p>
<p>After visiting London in the final week of February I got sick, and shortly after my girlfriend got sick as well. Even though COVID-19 was gaining momentum, I wasn't able to get a test back then, so I'm not sure if I had COVID or just a regular flu, but to err on the safe side we decided to stay inside for several weeks.</p>
<p>By the time we got out of quarantine, the rest of the country had gone into it. Many stores, gyms and theatres closed and restaurants had to switch to delivery only. Somehow the months of lockdown flew by in an instant, while also lasting forever. Time can be funny like that.</p>
<p>By summer, most lockdowns were lifted, so we decided to take a much needed break in the beautiful Italian town of Carloforte (population 6000). This small holiday made us realise what we had been missing the past months so once back we planned another much larger trip to visit our friends in Curaçao for a month.</p>
<p>Being there brought us back to the nomad life that we lived in 2019 and made us realise that we don't want to stay in Amsterdam forever. We'll likely still be here for a little while longer, as we do have a lot of friends and family that we like having close. But the goal is to be on a plane again sooner rather than later.</p>
<h2 id="events">🎙 Events</h2>
<p>Because of COVID-19, most in-person events I wanted to attend were cancelled or moved to the virtual space. I'm not a big fan of virtual events, but given the situation I decided to give them a try. And I still don't think they're a replacement for real in-person meetups. But I did have a pretty good time at the virtual events I attended.</p>
<h4 id="ethlondon">ETHLondon</h4>
<p>Before the world went into lockdown, I was able to go to the ETHLondon hackathon. Over there I teamed up with my girlfriend and some of our friends for an intense weekend of hacking up <a href="https://github.com/rkalis/radical.domains">Radical.domains</a>.</p>
<p>Radical Domains is an innovative ownership model for ENS domains that is based on Harberger taxes. Under this system ENS names are <em>always</em> for sale at a price set by its owner, and the owner pays a percentage-based tax/rent over this price. This incentivises productive use of the domain name and disincentivises the hoarding or &quot;squatting&quot; of domains.</p>
<h4 id="hackmoney">HackMoney</h4>
<p>Since all in-person events were cancelled I decided to give virtual hackathons a try. I initially expected that I wouldn't enjoy it as much, but I ended up with a great team and we had a lot of fun implementing LiquiDeFi, a contract that pools user's funds to execute liquidations on popular DeFi platforms such as Aave.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: embed--><figure class="kg-card kg-embed-card"><iframe width="200" height="113" src="https://www.youtube.com/embed/oWCJA2IUyes?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></figure><!--kg-card-end: embed--><!--kg-card-begin: markdown--><p>We ended up being chosen as one of the ten winners (or <em>finalists</em>) of the hackathons. After the hackathon we made some attempts to continue on with the project, including applying to Gitcoin's KERNEL program. In the end we didn't end up having the resources to pursue this project.</p>
<h4 id="bchdevcon3">BCHDEVCON 3</h4>
<p>The first hackathon I ever participated in was BCHDEVCON Amsterdam, organised by Eléonore Blanc back in 2018. So when she was organising the next iteration of the BCHDEVCON hackathon this September I knew that I could not miss it.</p>
<img src="https://kalis.me/content/images/2021/01/example.png" width="100%" style="border-radius: 0px" alt="🏠 2020 Year in Review: Staying Home, Vintage Furniture & DeFi">
<p>I teamed up with my UI/UX designer friend Jenny and together we built <a href="https://github.com/BCHDEVCON3/cashscript-playground">CashScript Playground</a>, an online IDE for writing CashScript contracts, with a generated UI to immediately interact with the contracts. It was inspired by Ethereum's Remix IDE, but it is much more basic.</p>
<h4 id="trufflecon2020">TruffleCon 2020</h4>
<p>Because TruffleCon was moved to the virtual space, I thought about skipping it this year. But ultimately I decided that it would be nice to give a short talk about truffle-plugin-verify, since I hadn't done a public walk through of the plugin yet.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: embed--><figure class="kg-card kg-embed-card"><iframe width="200" height="113" src="https://www.youtube.com/embed/AnJUqNd81TA?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></figure><!--kg-card-end: embed--><!--kg-card-begin: markdown--><p>While there were some interesting talks at TruffleCon 2020, the talks are never the main reason to go to any conference. And it is always difficult to really connect with people in virtual events. I did enjoy it, but I also hope we'll see an in-person TruffleCon 2021.</p>
<h2 id="stoopingams">📸 @stoopingams</h2>
<p>In 2019 I visited my friend Jenny in New York, where she introduced me to the <a href="https://www.instagram.com/stoopingnyc/">@stoopingnyc</a> instagram account. This account posts pictures of furniture that NYC residents put outside so that others can pick it up and give it new life. People who spot something send it to the maintainers of this account and they post the best items.</p>
<p>I started looking for similar accounts in Amsterdam, but when I couldn't find any I decided to start <a href="https://www.instagram.com/stoopingams/">@stoopingams</a> in April of 2020. At first it was just friends and family submitting photos, but it quickly grew to 1000+ followers.</p>
<img src="https://kalis.me/content/images/2021/01/stoopingams.jpg" width="100%" style="border-radius: 0px" alt="🏠 2020 Year in Review: Staying Home, Vintage Furniture & DeFi">
<p>Meanwhile the @stoopingnyc account exploded to more than 100k followers, and news outlets even picked up on it. Several outlets also reached out to me for some segments, so in 2020 @stoopingams was featured in <a href="https://www.bobblehaus.com/blogs/culture/a-stoop-by-stoop-guide-with-stoopingams">Bobblehaus</a>, <a href="https://www.today.com/video/new-yorkers-redecorate-amid-pandemic-90072133815">NBC's Today</a> and <a href="https://www.wsj.com/articles/stooping-city-street-furniture-11600717798">The Wall Street Journal</a>.</p>
<p>It has been really nice to see a kind of community developing around the account. There are people that regularly submit their finds and people share some amazing stories as well. Overall it has been a great way to get out of my software engineering bubble and get involved with something completely different.</p>
<h2 id="cryptocurrency">⛓ Cryptocurrency</h2>
<p>As 2020 has come to an end it looks like cryptocurrency is heading into a new bull market. BTC is at a new all time high and we've seen DeFi coins like AAVE and LINK increase more than tenfold in the past year.</p>
<p>In 2020, <a href="https://aave.com">Aave</a> and <a href="https://uniswap.org">Uniswap</a> launched their v2, while other projects like <a href="https://yearn.finance/">Yearn</a> and <a href="https://renproject.io/">Ren</a> had their mainnet launch this year as well. DeFi is expanding to other popular chains too with projects such as <a href="https://atomic.finance/odds/">Atomic Odds</a> on BTC and <a href="https://anyhedge.com/">AnyHedge</a>/<a href="https://detoken.net/?r=aZvkhRnDiag">Detoken</a> on BCH.</p>
<p>Infrastructure development has also continued to flourish. Ethereum 2.0 launched its <a href="https://ethereum.org/en/eth2/">beacon chain</a> and BTC is gearing up for its <a href="https://cointelegraph.com/news/bitcoin-s-taproot-is-ready-to-go-but-it-s-unlikely-to-be-included-in-the-next-release">Taproot upgrade</a>. Funding for open source projects has increased greatly. The latest <a href="https://gitcoin.co/grants/">Gitcoin Grants</a> round raised more than a million USD for open source projects and <a href="https://flipstarter.cash">FlipStarter</a> raised a similar amount in 2020.</p>
<h4 id="anyhedge">AnyHedge</h4>
<p>In 2019 I built CashScript, a smart contract language and SDK for Bitcoin Cash. Because of this, I was asked by <a href="https://generalprotocols.com">General Protocols</a> to join their team in developing <a href="https://anyhedge.com">AnyHedge</a>, the first DeFi project on Bitcoin Cash. They raised funding in May of this year, and I joined them shortly after.</p>
<p>AnyHedge can be seen as a risk-trading contract. Two parties can enter a contract where one party locks in the USD value of their BCH, while the other party gets leveraged exposure to the price movement of their BCH.</p>
<p>The smart contracts for AnyHedge are written in CashScript, so as part of the work at General Protocols I maintain CashScript, as well as contribute to other fundamental libraries such as electrum-cash. Besides this I work on the open source <a href="https://www.npmjs.com/package/@generalprotocols/anyhedge">AnyHedge library</a> and other parts of the AnyHedge stack.</p>
<p>The AnyHedge library and contracts have been available for several months, but on 30 December 2020 we also launched our first integration into the non-custodial <a href="https://detoken.net/?r=aZvkhRnDiag">Detoken</a> exchange. This allows regular users to use these contracts and get stability or leverage on their BCH.</p>
<h4 id="cashscript">CashScript</h4>
<p>One great thing about working on AnyHedge is that all of a sudden I was able to eat my own dog food with CashScript. By actually using CashScript for a non-trivial project myself, I discovered a lot of things that required improvement.</p>
<p>So over the course of 2020 I rolled out two major (and plenty minor) updates. Most importantly I <a href="https://cashscript.org/docs/releases/release-notes#v050">removed the dependency on BITBOX</a>, which has not been maintained for a while. Instead CashScript can now be used with a number of different libraries, while implementing adapters for other libraries is very easy.</p>
<p>Besides this I added support for <a href="https://cashscript.org/docs/releases/release-notes#v040"><code>OP_REVERSEBYTES</code> and bitwise operations</a>. I also completely refactored the <a href="https://cashscript.org/docs/releases/release-notes#v050">contract instantiation API</a> and the <a href="https://cashscript.org/docs/releases/release-notes#v040">transaction sending API</a>. Finally I made a plethora of quality of life improvements and tweaks, bug fixes and compiler optimisations.</p>
<h4 id="truffle">Truffle</h4>
<p>If you've read my previous <em>year in review</em> posts, you know that I am a big fan of the Truffle suite of Ethereum developer tools. TruffleCon 2018 was the first conference I ever spoke at, and I returned for TruffleCon 2019 and 2020. I wrote several libraries that integrate with Truffle, and use it in my own ETH projects as well.</p>
<p>So when Truffle reached out in December for some contracting work I was happy to accept the gig. In the coming months I'll be working with the Truffle team in a part time contracting setting to help with the Truffle+Filecoin integration.</p>
<p>This integration will allow developers to persist their dapp frontend on IPFS/Filecoin and spin up a local version of IPFS and the Filecoin blockchain for testing purposes. Part of the integration is an overhaul to the Truffle plugin system, something I am already quite familiar with through my work on truffle-plugin-verify.</p>
<h4 id="trufflepluginverify">truffle-plugin-verify</h4>
<p>While the initial release of truffle-plugin-verify was in 2019, this year brought some big improvements to the project. The most important update was v0.5.0, which was a complete overhaul of the plugin's functionality.</p>
<p>In earlier versions, the plugin flattened a contract's source file by concatenating all imported source files. The result was a huge source file that was difficult to navigate. So v0.5.0 uses so-called <em>Standard Input JSON</em>, in which the contents of all relevant files are stored in a structured JSON file. This results in Etherscan displaying all these files separately, making the verified code much easier to navigate.</p>
<p>The second important update is that besides Etherscan, the plugin now also supports source code verification for BscScan. BscScan is a block explorer similar to Etherscan, but for Binance Smart Chain (BSC) instead of Ethereum. So if you're writing smart contracts for BSC, you can use truffle-plugin-verify for source code verification.</p>
<h4 id="revokecash">revoke.cash</h4>
<p>Revoke.cash is a website that shows you a list of your outstanding ERC20 allowances, allowing you to re-evaluate them and revoke them if you no longer need them. I wrote an <a href="https://kalis.me/unlimited-erc20-allowances">in-depth article</a> that outlines the reasons why you'd want to do this.</p>
<p>Revoke.cash is also not a new project. But like truffle-plugin-verify, it did see some important updates in 2020. Notable changes are ENS support, token icons, a home-made logo, and design improvements.</p>
<p>Another notable update was integrating the <a href="https://tokens.kleros.io/">Kleros T2CR</a>, an on-chain curated registry of tokens. New tokens are added to this registry through a fully decentralised process. By using this registry, only registered tokens are displayed in revoke.cash's UI, while unregistered tokens are hidden behind a checkbox.</p>
<h2 id="opensource">🐙 Open Source</h2>
<p>When I reflected on my open source projects in 2019, one thing I noted is that I hadn't made any real contributions to other projects, while I had <em>received</em> many on my own projects. So I decided that I wanted to contribute to more projects in 2020. And I did.</p>
<p>It's always easier to think of important missing features or other pain points if you're an actual user of the project yourself. Which is why the contributions I made were all to libraries and tools that I use in my own work.</p>
<p>The Bitcoin Cash upgrade in May included a new opcode, <code>OP_REVERSEBYTES</code>. Because I wanted to support this new functionality in CashScript, I needed underlying libraries to support it as well. So to expedite that process I raised PRs to <a href="https://github.com/bitpay/bitcore/pull/2831">bitcore-lib-cash</a> and <a href="https://github.com/bitauth/libauth/pull/56">libauth</a> adding support for the new opcode.</p>
<p>As part of my work with General Protocols, I have been working with the <a href="https://electrumx-spesmilo.readthedocs.io/en/latest/">Electrum protocol</a> a lot, so when Chris Troutner wanted to integrate Electrum functionality into his <a href="https://fullstack.cash/">FullStack.cash</a> offering, I was happy to help out and <a href="https://github.com/Permissionless-Software-Foundation/bch-api/pull/17">raise</a> <a href="https://github.com/Permissionless-Software-Foundation/bch-api/pull/20">some</a> <a href="https://github.com/Permissionless-Software-Foundation/bch-api/pull/35">PRs</a> as well.</p>
<p>Earlier in the year I also wanted to get a bit more familiar with the Truffle codebase, so I picked up a <a href="https://github.com/trufflesuite/truffle/pull/2905">few</a> <a href="https://github.com/trufflesuite/truffle/pull/2909">small</a> <a href="https://github.com/trufflesuite/truffle/pull/2916">issues</a> and raised PRs for them. Turns out that was a good call, since it means I won't need to spend too much time getting familiar with Truffle's code for the upcoming Filecoin integration.</p>
<img src="https://kalis.me/content/images/2021/01/star-history.jpg" width="100%" style="border-radius: 0px" alt="🏠 2020 Year in Review: Staying Home, Vintage Furniture & DeFi">
<p>My own open source projects have also continued to gain popularity. Both truffle-assertions and truffle-plugin-verify passed the 100 star milestone on GitHub this year. My NPM packages now get a combined 25k+ monthly downloads, almost doubling last year's stats and especially truffle-plugin-verify has seen a big uptick in users.</p>
<img src="https://kalis.me/content/images/2021/01/npm-downloads.jpg" width="100%" style="border-radius: 0px" alt="🏠 2020 Year in Review: Staying Home, Vintage Furniture & DeFi">
<p>Of course no open source project is ever a one-man job, so I want to thank everyone who contributed to these projects in 2020, whether that is by opening issues, raising PRs or maintaining underlying libraries.</p>
<p>For truffle-plugin-verify I want to particularly thank <a href="https://github.com/xzjcool">xzjcool</a> for implementing BscScan verification into truffle-plugin-verify and the entire Etherscan team (but especially <a href="https://github.com/Enigmatic331">Enigmatic331</a>) for being responsive and solving issues on the Etherscan side. I also want to thank <a href="https://github.com/chriseth">chriseth</a> for being available to help out with any Solidity compilation quirks that come up now and then.</p>
<p>For CashScript I want to thank <a href="https://github.com/maikelmclauflin">Michael McLaughlin</a> for custom UTXO selection, <a href="https://github.com/alcipir">Andre Cabrera</a> for browser support, and <a href="https://github.com/2qx">2qx</a> for regtest support. I also want to thank <a href="https://github.com/dagurval/">Dagur Valberg Johansson</a> and  <a href="https://github.com/nyusternie">Shomari Prince</a> for their feedback and bug reports.</p>
<p>Overall I am happy with the direction that my open source projects are headed, and it is motivating to see usage rising across the board.</p>
<h2 id="2021">2021</h2>
<p>In 2020 I did a lot of work on my open source projects and I contributed to important open source libraries that I depend on as well. I joined General Protocols to work on the first iteration of DeFi apps on BCH. AnyHedge launched in the final days of 2020 and has seen almost $1M in volume in its first two weeks.</p>
<p>In the coming year I look forward to keep building on AnyHedge, my personal projects and other freelancing projects. I'm planning to go to EthCC in Paris in April, so I hope 2021 will see a resurgence of in-person events and travel as well.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Unlimited ERC20 allowances considered harmful]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>The latest big thing in Ethereum is Decentralised Finance (DeFi). Central applications of DeFi are lending, staking and trading ERC20 tokens. To use ERC20 tokens in DeFi protocols such as <a href="https://uniswap.org/">Uniswap</a>, <a href="https://aave.com/">Aave</a> or <a href="https://yearn.finance/">Yearn</a> you have to grant the dapp permission to spend tokens on your behalf - known as</p>]]></description><link>https://kalis.me/unlimited-erc20-allowances/</link><guid isPermaLink="false">5eb1664dc28b3503c57375b1</guid><category><![CDATA[ethereum]]></category><category><![CDATA[crypto]]></category><dc:creator><![CDATA[Rosco Kalis]]></dc:creator><pubDate>Wed, 09 Dec 2020 16:35:58 GMT</pubDate><media:content url="https://kalis.me/content/images/2020/11/allowance.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://kalis.me/content/images/2020/11/allowance.jpg" alt="Unlimited ERC20 allowances considered harmful"><p>The latest big thing in Ethereum is Decentralised Finance (DeFi). Central applications of DeFi are lending, staking and trading ERC20 tokens. To use ERC20 tokens in DeFi protocols such as <a href="https://uniswap.org/">Uniswap</a>, <a href="https://aave.com/">Aave</a> or <a href="https://yearn.finance/">Yearn</a> you have to grant the dapp permission to spend tokens on your behalf - known as an <em>ERC20 allowance</em>. These allowances are integral to the functioning of DeFi platforms, but can be dangerous if left unchecked.</p>
<p><em>Note: this post specifically talks about ERC20 allowances, but it also applies to NFTs.</em></p>
<h3 id="whyareerc20allowancesnecessary">Why are ERC20 allowances necessary?</h3>
<p>With Ethereum's native ETH token it is possible to call a smart contract function and send ETH to the contract at the same time. This is done using so-called <em>payable functions</em>. But because ERC20 tokens are smart contracts themselves, there is no way to <em>directly</em> send tokens to a smart contract <strong>while also calling one of its functions</strong>.</p>
<p>Instead, the ERC20 standard allows smart contracts to transfer tokens on behalf of users - with the <code>transferFrom()</code> function. To do so, the user needs to <em>allow</em> the smart contract to transfer those tokens on their behalf.</p>
<p>This way, a user can &quot;deposit&quot; tokens into a smart contract, and at the same time, <strong>the smart contract can update its state to reflect the deposit</strong>. In contrast, if you just send ERC20 tokens to the contract, the contract cannot update its state (e.g. to credit the deposit to your account).</p>
<img src="https://kalis.me/content/images/2020/12/aave-busd-deposit.png" alt="Unlimited ERC20 allowances considered harmful" width="100%">
<p>As an example, if you want to &quot;deposit&quot; DAI into Aave to earn interest, you need to first <em>allow</em> the Aave contract to take some DAI from your wallet. Then you call a smart contract function on the Aave contract where you specify how much DAI you want to deposit. The Aave contract then takes exactly that amount from your wallet using the <code>transferFrom()</code> function and credits you with the same amount of aDAI tokens.</p>
<h3 id="whyareunlimitederc20allowancesharmful">Why are <em>unlimited</em> ERC20 allowances harmful?</h3>
<p>When depositing a specific amount (say 100 DAI) into a contract, you can choose to set an allowance of <em>exactly</em> that amount. But instead, many apps instead request an <em>unlimited</em> allowance from the user.</p>
<p>This offers a <strong>superior user experience</strong> because the user does not need to approve a new allowance <em>every time</em> they want to deposit tokens. By setting up an unlimited allowance, the user just needs to approve it once, and not repeat the process for subsequent deposits.</p>
<p>However, this setup comes with significant drawbacks. As we know, bugs can exist and exploits can happen even in established projects. And by giving these platforms an unlimited allowance, you do not only expose your deposited funds to these risks, but <strong>also the tokens that you're holding &quot;safely&quot; in your wallet</strong>.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: embed--><figure class="kg-card kg-embed-card"><iframe width="480" height="270" src="https://www.youtube.com/embed/y9A8wHhNjJA?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></figure><!--kg-card-end: embed--><!--kg-card-begin: markdown--><p>I first talked about this with Paul Berg at Devcon 5, where he gave a presentation about precisely the issues discussed in this article. While developing <a href="https://sablier.finance">Sablier</a>, Paul found (and fixed!) a bug in his smart contracts, where not only the <em>deposited</em> DAI ($100) were at risk, but also <em>all</em> DAI in the testers' wallets ($10k)!</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h3 id="realworldrisks">Real world risks</h3>
<p>For a long time the risk of unlimited allowances was largely theoretical, and Paul's Sablier bug was fixed before the platform went into production. Back then there hadn't been any exploits that took advantage of ERC20 allowances, but it was bound to happen as platforms kept using unlimited allowances.</p>
<p>And over the past years we have seen several such exploits.</p>
<h4 id="bugexploits">Bug exploits</h4>
<p>In early 2020, <a href="https://blog.bancor.network/bancors-response-to-today-s-smart-contract-vulnerability-dc888c589fe4">Bancor experienced a bug</a> that put users' funds at risk. The function that executes the ERC20 <code>transferFrom()</code> function was accidentally made public (rather than private to the contract), <strong>which allowed anyone to execute it and drain the users' wallets</strong>. Bancor performed a white-hat hack of the contract to contain the damage and returned the funds back to users.</p>
<p>Another high profile bug exploit was the <a href="https://cointelegraph.com/news/transaction-batching-protocol-furucombo-suffers-14-million-evil-contract-hack">Furucombo hack</a> in February 2021, where a bug in the Furucombo protocol enabled a hacker to drain the wallets of people who gave an allowance to Furucombo, even if they didn't have any funds deposited into the contract directly.</p>
<h4 id="maliciousprojects">Malicious projects</h4>
<p>While bugs can happen even in reputable and legitimate projects, there have also been cases where the project itself was malicious. During 2020's DeFi summer, people were jumping at every new food-themed DeFi fork, which included some outright scams. And even if people tried to limit their risk by only depositing small amounts, funds in their wallets were still at risk because of <em>unlimited ERC20 allowances</em>.</p>
<img src="https://kalis.me/content/images/2020/12/unicats-interface.png" alt="Unlimited ERC20 allowances considered harmful" width="100%">
<p>ZenGo reported <a href="https://medium.com/zengo/unicats-go-phishing-eaf39ff9da64">one such exploit</a> in a project called UniCats. People could deposit their Uniswap (UNI) tokens to farm MEOW tokens (you can't make this up). But to do the deposit, they had to grant an <em>unlimited allowance</em>. When the project inevitably rug-pulled, the scammers not only took the deposited funds, but <strong>also all UNI tokens that users had in their wallets</strong>.</p>
<p><a href="https://twitter.com/nomos_paradox/status/1299215849018937345">Another such case</a> happened with a project called Degen Money, which used a slightly less sophisticated (but not less effective) approach. Rather than developing their own smart contract, they created a frontend that made two approval transactions. One to a functioning contract, and one to a completely different address.</p>
<p>Because many people do not specifically check the contract addresses, this allowed the attackers to drain users' wallets.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h3 id="whatabouthardwarewallets">What about hardware wallets?</h3>
<p>In general, hardware wallets (such as the <a href="https://shop.ledger.com/pages/ledger-nano-x?r=8317e54b3758">Ledger Nano X</a>) are much safer than mobile or browser-based wallets. The reason for this is that the private keys that control the funds are securely stored on the hardware wallet and never leave the device. So by using a hardware wallet you ensure that no one can steal your private keys.</p>
<p>The problem with ERC20 allowances though, is that <strong>no one <em>needs</em> to steal your private keys</strong> to take the tokens from your wallet. And because of that, hardware wallets offer no protection whatsoever to the exploits discussed in this article.</p>
<p>It is still good practice to use a hardware wallet because they do protect you against a range of other possible exploits. But you need to be aware that they do not protect against allowance exploits or many other smart contract exploits.</p>
<h3 id="whatcandappdevelopersdo">What can dapp developers do?</h3>
<p>In his talk at Devcon, Paul mentioned several possible solutions to the unlimited allowance problem, which all have different strengths and drawbacks. The most practical of those solutions is using the approve-spend pattern. In this pattern you only request the user to approve the <em>exact</em> amount that they want to use at that moment, rather than an unlimited amount.</p>
<p>This is a worse user experience since the user needs to send a new approval transaction every time they want to send a transaction, rather than doing one single approval. This also has the added drawback that it costs more in transaction fees, which can be especially troublesome when fees rise like they did last summer.</p>
<p>So a better option is to offer the <em>choice</em> to users where they can either choose to approve <em>only</em> what they need to spend at the time, or they can approve a much larger amount if they intend to do more transactions in the near future. This strategy is already employed by several projects, such as <a href="https://zapper.fi">Zapper.fi</a> and <a href="https://curve.fi">Curve.fi</a>.</p>
<img src="https://kalis.me/content/images/2020/12/curve-allowance.png" alt="Unlimited ERC20 allowances considered harmful" width="80%">
<p>An alternative solution to mitigate transaction costs is adopting <a href="https://eips.ethereum.org/EIPS/eip-2612">EIP2612 (permit)</a>. This standard enables users to sign a message to set their allowance (which is free), rather than having to send a transaction to do so. The developer ecosystem around EIP2612 is small, but it is growing rapidly and projects such as <a href="https://uniswap.org/">Uniswap</a> are using it for their lending provider tokens.</p>
<h3 id="whatcanusersdo">What can users do?</h3>
<p>Since ERC20 allowances are integral to the functioning of many smart contracts, it is not an option to stop approving allowances altogether. But where possible, try to avoid <em>unlimited</em> allowances.</p>
<p>People are more aware of this issue than a year ago, so some dapps offer the option to <em>only</em> approve the amount you're spending at the moment, but most apps still don't. Even then, advanced users can lower their allowance through Metamask's interface.</p>
<img src="https://kalis.me/content/images/2020/12/metamask-update-allowance.jpg" alt="Unlimited ERC20 allowances considered harmful" width="80%">
<p>When you're using a dapp try to consider if you're going to be using this dapp a lot and you trust the project (unlimited allowance), or if you'll use dapp infrequently or you do not trust the project (smaller allowances). In either case it is also a good practice to review your outstanding allowances periodically (say every month), and revoke those that you're not actively using any more.</p>
<img src="https://kalis.me/content/images/2023/02/revokecash.png" alt="Unlimited ERC20 allowances considered harmful" width="100%">
<p>To inspect and revoke these allowances, I developed a tool called <a href="https://revoke.cash">Revoke.cash</a>. It gives an overview of an address' token balances and corresponding allowances. Allowances can then easily be revoked or lowered.</p>
<h4 id="howisrevokingdifferentfromdisconnectingmetamask">How is <em>revoking</em> different from <em>disconnecting MetaMask</em>?</h4>
<p>MetaMask has an option to <em>disconnect</em> any accounts that you previously connected to a website. Some people think that using this <em>disconnect</em> button will protect them from exploits. <strong>This is not the case.</strong></p>
<p>Disconnecting MetaMask does not do <em>anything</em> to protect you from allowance exploits - or most other exploits. The only thing that you achieve when disconnecting MetaMask from a website is that that website cannot <em>see</em> your address any more.</p>
<h3 id="conclusions">Conclusions</h3>
<p>Allowances are necessary for the functioning of many decentralised applications, but at the same time <em>unlimited</em> allowances are generally harmful to security. We've seen several exploits in the past two years that take advantage of ERC20 allowances, and the awareness of the issue is much higher today than it was in 2019. There are a few things you can do as a user to mitigate the discussed risks, including periodically reviewing and <a href="https://revoke.cash">revoking outstanding allowances</a>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[🏖 2019  Year in Review: Graduation, Cryptocurrency & Traveling]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>I wrote my first year review in 2018 because I felt that a lot had happened and I wanted to reflect. <strong>And I feel the same about 2019.</strong> A lot changed in my life as I shifted from being a student to working in the cryptocurrency space. In this article</p>]]></description><link>https://kalis.me/2019-year-in-review/</link><guid isPermaLink="false">5c35cf577a534010da6e8550</guid><category><![CDATA[life]]></category><category><![CDATA[year-review]]></category><dc:creator><![CDATA[Rosco Kalis]]></dc:creator><pubDate>Fri, 17 Apr 2020 14:36:30 GMT</pubDate><media:content url="https://kalis.me/content/images/2020/04/2019-year-in-review.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://kalis.me/content/images/2020/04/2019-year-in-review.jpg" alt="🏖 2019  Year in Review: Graduation, Cryptocurrency & Traveling"><p>I wrote my first year review in 2018 because I felt that a lot had happened and I wanted to reflect. <strong>And I feel the same about 2019.</strong> A lot changed in my life as I shifted from being a student to working in the cryptocurrency space. In this article I reflect on these changes.</p>
<p>I'm publishing this article rather late, given that we're already in the second quarter of 2020. I didn't manage to complete the article early in the year, but <strong>it is never too late to look back and reflect</strong>. Even three months later. I try to keep the focus on 2019, leaving events of the past three months for next year's reflection.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h2 id="writing">✍️ Writing</h2>
<p>Last year I noted how my writing had slowed down in 2018 with just three articles. And this year <strong>my writing has not been more productive</strong>. But while I saw it as a negative point last year, <strong>I'm actually quite content</strong> with my writing the past year. I've decided that it's alright like this. There's no need to pump out new articles every month, and it's fine to take some time.</p>
<p>Last year I listed three articles that were still in the pipeline for 2019. And guess what? <strong>None of them got published</strong>. But I realise why. At first I wanted to write these articles just to help out people with the same problems as me. But while I was writing them, I realised that these posts would not be publish-and-forget.</p>
<p>They would need support and updates after publishing. And I was not interested enough in the topics to justify spending that time. So <strong>instead I focused on topics that <em>did</em> interest me enough</strong> to keep them up to date.</p>
<p>This led me to publish three different articles centred around smart contracts.</p>
<ul>
<li><a href="https://kalis.me/verify-truffle-smart-contracts-etherscan/">Automatically verify Truffle smart contracts on Etherscan</a></li>
<li><a href="https://kalis.me/creating-truffle-plugins/">Creating Truffle plugins</a></li>
<li><a href="https://kalis.me/smart-contracts-eth-btc-bch/">Smart Contracts on Ethereum, Bitcoin and Bitcoin Cash</a></li>
</ul>
<p>This time around I won't be listing the articles that I might or might not publish in 2020. I've learned from my mistakes of last year. But if you're still interested to read about anything in particular, let me know in the comments below.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h2 id="graduation">🎓 Graduation</h2>
<p>I've been a student for most of my adult life. I started my Bachelor's degree in Computer Science when I was seventeen, and completed it in <a href="https://kalis.me/2018-year-in-review">2018</a>. I immediately continued with a Master's degree in Software Engineering.</p>
<p>I had some doubts going into the Master's program, as I'd rather spend more time on <strong>actual software engineering</strong> rather than lectures. But the degree I chose to pursue is very practical and is a only one-year program, so I decided to go for it.</p>
<p>Because the program has a practical focus, part of the work is doing a project at a company and write a thesis on it. I chose to do my project at <a href="https://bitcoin.com/">Bitcoin.com</a>, where I joined <a href="https://twitter.com/cgcardona">Gabriel Cardona</a>'s team <strong>to create a high-level smart contract language</strong> for Bitcoin Cash, called <a href="https://cashscript.org/">CashScript</a>.</p>
<p><strong>The project and thesis were well-received</strong> by the company and the graduation board. So after defending my thesis in late August I finished the project with a 9/10. My supervisor urged me to publish a paper, as I had done in 2018 with my BSc thesis. While this did interest me, the months that followed proved to be filled with many other exciting things. So in the end I decided to forgo another publication.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h2 id="blockchaincryptocurrency">⛓ Blockchain &amp; Cryptocurrency</h2>
<p>As with 2018, this year was filled to the brim with blockchain and cryptocurrency projects. Besides my biggest project CashScript, I created <a href="https://github.com/rkalis/truffle-plugin-verify">truffle-plugin-verify</a> and <a href="https://revoke.cash">revoke.cash</a>, and maintained my previous project, <a href="https://github.com/rkalis/truffle-assertions">truffle-assertions</a>. I also attended cryptocurrency events and was featured in several publications.</p>
<h4 id="cashscript">CashScript</h4>
<p>In early April <strong>I flew over to Tokyo</strong> to work on CashScript at the Bitcoin.com office. In the months that followed I researched Bitcoin, Bitcoin Cash and Ethereum, and I did a deep dive into Bitcoin Script, the VM that powers BTC and BCH transactions.</p>
<p>CashScript's syntax was <strong>inspired by Ethereum's <a href="https://solidity.readthedocs.io/en/v0.6.6/">Solidity</a></strong> to allow for more collaboration between the Ethereum and Bitcoin Cash communities. The CashScript SDK was also inspired by several Ethereum projects, including <a href="https://www.trufflesuite.com/">Truffle</a> and <a href="https://web3js.readthedocs.io/en/v1.2.0/index.html">web3.js</a>.</p>
<img src="https://kalis.me/content/images/2020/04/cashscript.jpg" width="80%" style="border-radius: 5px" alt="🏖 2019  Year in Review: Graduation, Cryptocurrency & Traveling">
<p>Since then CashScript has <strong>greatly simplified smart contract development in Bitcoin Cash</strong>, and has made complex techniques like <em>covenants</em> accessible. The full compiler stack as well as the SDK were implemented using TypeScript for strong integration within Node.js and browser applications.</p>
<p>If you're interested in learning more about the differences in smart contracts on Bitcoin, Bitcoin Cash and Ethereum and where CashScript fits in, read my article <a href="https://kalis.me/smart-contracts-eth-btc-bch/"><em>Smart contracts on Ethereum, Bitcoin and Bitcoin Cash</em></a>.</p>
<h4 id="trufflepluginverify">truffle-plugin-verify</h4>
<p>Around the same time I started working on CashScript I also created <strong>one of the first widely used Truffle plugins</strong>: <a href="https://www.npmjs.com/package/truffle-plugin-verify">truffle-plugin-verify</a>. The plugin allows you to take any smart contract in your Truffle projects and submit its source code to <a href="https://etherscan.io/">Etherscan</a> for verification. Read more about this process in my article <a href="https://kalis.me/verify-truffle-smart-contracts-etherscan/"><em>Automatically verify Truffle smart contracts on Etherscan</em></a></p>
<p>Before this plugin, source code verification was only possible through a convoluted process that forced you to use Remix for contract deployment. And since Truffle has built-in functionality for contract compilation and deployment it would be unfortunate to throw that out of the window.</p>
<p>So instead truffle-plugin-verify adds a single command to the Truffle CLI that <strong>allows you to seamlessly verify the source code</strong> of any of the contracts deployed using Truffle. The plugin just needs to be set up with an Etherscan API key after which it can be used.</p>
<pre><code class="language-shell">truffle run verify SimpleStorage --network goerli
</code></pre>
<h4 id="truffleuniversity">Truffle University</h4>
<p>The plugin slowly began to gain traction, but it was one of the few Truffle plugins that was actually being used. Since there weren't many resources and guides on how to create Truffle plugins, <strong>I decided to contribute some of my own knowledge</strong>.</p>
<img src="https://kalis.me/content/images/2020/04/truffle-university.png" width="20%" style="border-radius: 5px" alt="🏖 2019  Year in Review: Graduation, Cryptocurrency & Traveling">
<p>I was asked to be a guest lecturer on Truffle University, a course for learning Ethereum smart contract development. I gave a hands-on workshop on Truffle plugin development, where we went through the entire process of developing a non-trivial Truffle plugin. The code for this workshop can be found <a href="https://github.com/rkalis/truffle-plugin-workshop">on GitHub</a>, and I later wrote <a href="https://kalis.me/creating-truffle-plugins/">an article about the same subject</a>.</p>
<h4 id="revokecash">revoke.cash</h4>
<p>Later on in the year at Devcon V in Osaka, I finally met <a href="https://twitter.com/PaulRBerg">Paul Berg</a> in person after previously having contact online. We talked a lot and one thing that stuck with me was the <a href="https://youtu.be/y9A8wHhNjJA">risk of ERC20 allowances in DeFi</a>.</p>
<p>Many DeFi applications - like <a href="https://uniswap.exchange/">Uniswap</a>, <a href="https://compound.finance/">Compound</a> or <a href="https://sablier.finance/">Sablier</a> - need to spend ERC20 tokens on the user's behalf. This is necessary for the functioning of these platforms, but <strong>could be dangerous</strong> if left unchecked.</p>
<img src="https://kalis.me/content/images/2020/03/revokecash.jpg" width="80%" style="border-radius: 5px" alt="🏖 2019  Year in Review: Graduation, Cryptocurrency & Traveling">
<p>If a bug is found in one of the smart contracts that has an active allowance, tokens can be taken from the &quot;safety&quot; of your wallet. So while these allowances are necessary when using the DeFi applications, but <strong>it is good practice to revoke outstanding allowances</strong> when you're not using them. <a href="https://revoke.cash">Revoke.cash</a> gives you an overview of all outstanding ERC20 allowances so they can be revoked.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h2 id="opensource">🐙 Open Source</h2>
<p>Almost all of my personal and professional projects are open source. Especially in cryptocurrency open-source software is important. The software can be dealing with large amounts of funds and even users' keys (in case of wallets), so <strong>this code should be publicly auditable</strong>.</p>
<img src="https://kalis.me/content/images/2020/04/github-stars.png" width="90%" style="border-radius: 5px" alt="🏖 2019  Year in Review: Graduation, Cryptocurrency & Traveling">
<p>My open source projects have increased in popularity, with CashScript and truffle-plugin-verify getting close to 50 GitHub stars within the year and truffle-assertions doubling in stars. But more impressive is the fact that my NPM packages now get a combined <strong>15k+ monthly downloads</strong> (mostly truffle-assertions). And that they're being used in big open source projects, such as <a href="https://gnosis.io/">Gnosis</a>, <a href="https://kyber.network/">Kyber</a> and <a href="https://renproject.io/">Ren</a>.</p>
<img src="https://kalis.me/content/images/2020/04/npm-downloads.png" width="80%" style="border-radius: 5px" alt="🏖 2019  Year in Review: Graduation, Cryptocurrency & Traveling">
<p>All projects also attracted outside contributors, so I'd like to thank <a href="https://github.com/alcipir">Andre Cabrera</a> and <a href="https://github.com/cgcardona">Gabriel Cardona</a> for contributing to CashScript, <a href="https://github.com/leonprou">Leon Prouger</a> for contributing to truffle-assertions and <a href="https://github.com/jijordre">Jens Jørdre</a>, <a href="https://github.com/katat">Kata Choi</a>, <a href="https://github.com/vinceau">Vincent Au</a>, <a href="https://github.com/SeanJCasey">Sean Casey</a> and <a href="https://github.com/tcoulter">Tim Coulter</a> for contributing to truffle-plugin-verify.</p>
<p>But while my own projects grew and many people contributed, <strong>I contributed less to other open source projects</strong> than I did in 2018. I did help out with some things on <a href="https://github.com/Bitcoin-com/bitbox-sdk">BITBOX</a>, <a href="https://kickback.events/">Kickback</a> and <a href="https://atomic.loans/">Atomic Loans</a> and I picked up my first Gitcoin bounty. Nonetheless, I want to contribute to some more open source projects in 2020.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h2 id="events">🎙 Events</h2>
<p>The first time I spoke at a conference was in 2018 at TruffleCon. And a month later I participated won my first hackathon at the Amsterdam BCH Hackathon. I enjoyed these events a lot, so it's unsurprising that I continued this trend throughout 2019.</p>
<p>Just before I left for Tokyo, I went to EthCC in Paris, followed by ETHParis. The company I worked for at the time has housing in Paris so I was able to stay there the entire week 🎉! I repeated the talk I gave at TruffleCon 2018 in front of a new audience, and it was interesting to see the difference in reactions.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: embed--><figure class="kg-card kg-embed-card"><iframe width="480" height="270" src="https://www.youtube.com/embed/GON3qyFdUtE?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></figure><!--kg-card-end: embed--><!--kg-card-begin: markdown--><p>At ETHParis I hacked with people I had met at previous events, and we managed to win the JPMorgan Quorum prize. While I enjoyed the hackathon, we were too focused on winning a specific prize, rather than building something cool. So in the future <strong>my focus is on building an awesome product first</strong>, winning prizes second.</p>
<p>During my time in Japan I was mainly focused on my project, so I didn't attend any events there. But after leaving Japan at the end of July, I returned to TruffleCon in Seattle, where I gave a talk like the one I did for Truffle University. It was great to be back at TruffleCon this year. It remains <strong>one of my favourite events in the crypto space</strong> as it has a big focus on practical topics.</p>
<img src="https://kalis.me/content/images/2019/08/trufflecon2019-talk.jpg" width="80%" style="border-radius: 5px" alt="🏖 2019  Year in Review: Graduation, Cryptocurrency & Traveling">
<p>TruffleCon 2018 was in downtown Portland, so it was very easy to grab a bite or a drink after the talks. There were also some organised social events around the conference, making it easy to socialise. This year there were fewer official social events, and the conference took place outside the city, so it was more difficult to go out afterwards. Not that it stopped us!</p>
<p>After our time in the US we briefly returned home to Amsterdam. But this didn't last long, as I was invited to come over to Townsville, Australia for the Bitcoin Cash City conference. What I liked about this event is that it had <strong>the same developer focus</strong> that I was used to with events like TruffleCon. So it was great to share this vibe with some of the awesome people in the BCH community.</p>
<img src="https://kalis.me/content/images/2020/04/devcon.jpg" width="80%" style="border-radius: 5px" alt="🏖 2019  Year in Review: Graduation, Cryptocurrency & Traveling">
<p>We decided to stay in Asia a bit longer and in October I attended Devcon V in Osaka, the quintessential yearly Ethereum conference. I met some great people and I reconnected with many people I knew already.</p>
<p>I gave some (slightly work-in-progress) talks at side events and the Devcon community stage about the differences between smart contracts on Ethereum and Bitcoin Cash. It's quite difficult to articulate the most important differences, since Ethereum, Bitcoin, and Bitcoin Cash are fundamentally different beasts.</p>
<img src="https://kalis.me/content/images/2019/12/london-bch-1.jpg" width="80%" style="border-radius: 5px" alt="🏖 2019  Year in Review: Graduation, Cryptocurrency & Traveling">
<p>After travelling around for a bit longer, my final event of the year was the London Bitcoin Cash meetup. By that time I had gone through several more iterations of the same talk, and I presented on the differences between ETH, BTC and BCH in terms of smart contracts. The talk was well-received and I published its contents in my article <a href="https://kalis.me/smart-contracts-eth-btc-bch/"><em>Smart contracts on Ethereum, Bitcoin and Bitcoin Cash</em></a>.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h2 id="travel">🛩 Travel</h2>
<figure class="kg-card kg-image-card kg-width-full">
    <img src="https://kalis.me/content/images/2020/04/travel-2019.jpg" class="kg-image" alt="🏖 2019  Year in Review: Graduation, Cryptocurrency & Traveling">
</figure>
<p>While many things happened in 2019, the biggest change was <strong>spending more time outside of The Netherlands than inside</strong>. This started with my project in Tokyo, and continued with all the events I wanted to attend in the second half of the year. But in between those events we also decided to travel around for fun.</p>
<p>We traveled around Japan (Tokyo, Kyoto, Kamakura), with some smaller trips to Korea, Taiwan and Thailand. After leaving Japan we traveled through some of the US (Hawaii and the West Coast). After getting back to The Netherlands for two weeks, we flew over to Australia, then  to Bali.</p>
<p>From Bali we traveled on to Osaka for Devcon after which we revisited Korea and the US again (Chicago, Denver and NYC this time). From New York we flew over to London, and finally completing our second time around the world back to Amsterdam at the end of 2019.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h2 id="2020">📈 2020</h2>
<p>2019 was full of change for me. It marked an end to my 5 years of university and I went from living in Amsterdam to traveling the world. In my previous year review I wrote that I would speak at more conferences, travel to more places and graduate. <strong>And I did.</strong></p>
<p>With the current situation around COVID-19 it is good to be steady in the same location for a while, so <strong>I can't look ahead and make those predictions again</strong>. It will take some time for travel to resume and many events have been canceled, postponed or moved to the virtual space.</p>
<p>This could be a nice opportunity to take part in more virtual events, but I don't think they are a real replacement for actual personal contact. In any case I am looking forward to an exciting 2020 (<strong>but perhaps not too exciting!</strong>).</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Building love2d games for the web with love.js and Docker]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>A few years back I created a few small video games using the Lua-based game development framework <a href="https://love2d.org/">LÖVE (or love2d)</a>. Because they are so small, it doesn't make sense to distribute these games for desktop platforms. But they are great as browser games, so I used <a href="http://tannerrogalsky.com/">Tanner Rogalsky's</a> <a href="https://github.com/TannerRogalsky/love.js/">love.js</a></p>]]></description><link>https://kalis.me/building-love2d-games-web-docker/</link><guid isPermaLink="false">5e8359e3c28b3503c5736a04</guid><category><![CDATA[gamedev]]></category><dc:creator><![CDATA[Rosco Kalis]]></dc:creator><pubDate>Mon, 06 Apr 2020 14:12:27 GMT</pubDate><media:content url="https://kalis.me/content/images/2020/04/building-lovejs-1.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://kalis.me/content/images/2020/04/building-lovejs-1.png" alt="Building love2d games for the web with love.js and Docker"><p>A few years back I created a few small video games using the Lua-based game development framework <a href="https://love2d.org/">LÖVE (or love2d)</a>. Because they are so small, it doesn't make sense to distribute these games for desktop platforms. But they are great as browser games, so I used <a href="http://tannerrogalsky.com/">Tanner Rogalsky's</a> <a href="https://github.com/TannerRogalsky/love.js/">love.js</a> to convert the games to JavaScript.</p>
<p>The first game I published this way was my <a href="http://minesweeper.kalis.me">Minesweeper clone</a>. This was years back, using LÖVE version <code>0.10.0</code>. Back then the process was more complicated, but also more reliable. Now the love2d engine is at version <code>11.3</code> (they dropped the leading zero), and sadly <strong>love.js has not been able to keep up</strong>.</p>
<p>Despite this, <strong>it <em>is</em> still possible to build your LÖVE games using love.js</strong>, which I recently went through for my <a href="http://tetris.kalis.me">Tetris clone</a>. But there are some things to look out for. So in this article we will go through the required steps, and add an extra section for troubleshooting common errors.</p>
<h2 id="beforewestart">Before we start</h2>
<p>Although you technically don't need to have LÖVE or Lua installed for the actual build process, I do recommend installing it when developing LÖVE games 😉! Besides that, make sure that you have <a href="https://nodejs.org/en/">Node.js</a> installed so you can use NPM. Then use NPM to install the latest version of <code>love.js</code>.</p>
<pre><code class="language-shell">npm install -g love.js
</code></pre>
<p>If you want to deploy your game using <a href="https://www.docker.com/">Docker</a>, make sure to install it as well, but this is optional since you can also publish your game to the web without it.</p>
<h2 id="buildingawebversionofthegame">Building a web version of the game</h2>
<p>With love.js installed we can build our love game for the web. We can do this with either a game directory or a <code>.love</code> file. It is recommended to use a <code>.love</code> file as this makes sure that only the necessary files are included, keeping the bundle size smaller.</p>
<h3 id="1makethegamefileslovejscompatible">1. Make the game files love.js compatible</h3>
<p>Not all current Lua and LÖVE functionality is available in love.js, but luckily most is. These bullet points discuss the most important limitations. This list has information from love.js, LoveWebBuilder, the LÖVE forums, and my own experiences.</p>
<ul>
<li>Love.js can only be used for smaller games. Although there is no limit, larger games have a <strong>higher chance of crashing or bugs</strong>.</li>
<li>If the <code>t.version</code> in your <code>conf.lua</code> file is set to something other than <code>0.11.0</code>, your game might fail to run. This can be fixed by <strong>commenting out <code>t.version</code> before building</strong>.</li>
<li>The canvas size of your game cannot be set or reset from your game code, so make sure that you <strong>set the dimensions in your <code>love.conf</code> file</strong> with <code>t.window.width</code> and <code>t.window.height</code>.</li>
<li>Threads are unavailable in the browser, this means that LÖVE threads and audio streams cannot be used. So make sure that you're not using <code>love.thread</code> and that <strong>all audio sources are set to type &quot;static&quot;</strong>.</li>
<li><a href="https://luajit.org/ext_ffi.html">FFI</a> is not supported, so using FFI in your game files, or using any libraries that use it is not supported.</li>
<li>Love.js does not work with system-installed libraries, like those installed with Luarocks. Instead, <strong>all libraries should be included as Lua files</strong> in your game directory.</li>
</ul>
<h3 id="2createalovefile">2. Create a .love file</h3>
<p>We can create a <code>.love</code> file by bundling all game files in a ZIP file and giving it a <code>.love</code> extension. Depending on your platform, this can be done in different ways. There is usually an option in your file explorer, but on Linux and macOS you can also use the <code>zip</code> command line utility.</p>
<pre><code class="language-shell">zip -9 -r &lt;game name&gt;.love &lt;game files&gt;
</code></pre>
<p>You can put the whole directory in this ZIP file, but the idea is to only include the necessary game files. In case of packaging my Tetris clone, it looked like this.</p>
<pre><code class="language-shell">zip -9 -r Tetris.love assets/ lib/ src/ main.lua conf.lua
</code></pre>
<h3 id="3generateyourgamesjavascriptcode">3. Generate your game's JavaScript code</h3>
<p>With the <code>.love</code> file generated and love.js installed, we can easily generate the corresponding JavaScript and HTML on the command line.</p>
<pre><code class="language-shell">love.js &lt;love file&gt; &lt;output dir&gt; --title &lt;title&gt; --memory &lt;max memory&gt;
</code></pre>
<p>When running the command it is important to set the <code>--memory</code> flag to a high enough value as this cannot be changed after the build. Generally the default option of 16MB should be doable, but it can't hurt to go a bit higher. For my Tetris game I just opted for 64MB as a ceiling.</p>
<pre><code class="language-shell">love.js Tetris.love ./dist --title &quot;Tetris&quot; --memory 67108864
</code></pre>
<p>This outputs the generated code in the <code>./dist</code> directory, from where it can be copied over to a web server or tested locally. <strong>You do need to run a web server</strong> to run the game though, you cannot just open <code>index.html</code> and be done with it. But a simple Python web server should be fine.</p>
<pre><code class="language-shell">python -m SimpleHTTPServer 3000
</code></pre>
<p>After running the web server you can access the game in a browser at <code>localhost:3000</code>.</p>
<h3 id="4dockerisethebuildprocess">4. Dockerise the build process</h3>
<p>Now this next step is entirely optional. If you want you can just copy over your generated code to any web server to host it, but Docker is a great tool for simplifying deployments. By Dockerising you essentially automate the build process, which <strong>saves time if you need to repeat the process</strong>.</p>
<p>The way Docker works is we create a <em>Dockerfile</em>, which is a series of instructions that tells Docker what files it needs, and what it needs to do with these files to build and run our game.</p>
<h4 id="createadockerfile">Create a Dockerfile</h4>
<p>We start by creating a file named <code>Dockerfile</code> in the root game directory, which will tell Docker how it should package our game into a <em>Docker image</em>, which can then be deployed in a <em>Docker container</em>.</p>
<p>Once we created the Dockerfile, we tell Docker to base our image on an existing <em>public</em> Dockerfile, called <code>node:10</code>. We add <code>as build</code>, which gives the Docker image the name <em>build</em>, so we can refer to this Docker image later on.</p>
<pre><code class="language-docker">FROM node:10 as build
</code></pre>
<h4 id="copythegamefiles">Copy the game files</h4>
<p>Next we add a few lines to the <code>Dockerfile</code> to copy over all game files to the Docker image. This is similar to creating a <code>.love</code> file, but we put all game files in a folder named <code>/app/src</code> instead of a ZIP file.</p>
<p>After copying, we use the <code>sed</code> command to automatically comment out the <code>conf.lua</code> line containing <code>t.version</code>. This is necessary due to the caveat discussed in step 1.</p>
<pre><code class="language-docker">WORKDIR /app/src
COPY assets ./assets/
COPY lib ./lib/
COPY src ./src/
COPY main.lua conf.lua ./
RUN sed -i &quot;s/t.version/-- t.version/&quot; conf.lua
</code></pre>
<h4 id="buildingthegamewithlovejs">Building the game with love.js</h4>
<p>In the previous steps we told Docker what files it needs. Now we tell Docker what it needs to do with these files. In our case, it should install <code>love.js</code>  and run the correct <code>love.js</code> command, like we did manually in step 3. This will take the game files found in <code>/app/src</code> and place the generated JavaScript game in <code>/app/dist</code>.</p>
<pre><code class="language-docker">RUN npm install -g love.js
RUN love.js /app/src /app/dist --title &quot;Tetris&quot; --memory 67108864
</code></pre>
<h4 id="servingthegeneratedfiles">Serving the generated files</h4>
<p>After the build process is completed, we need to tell Docker how it should serve the generated files. For this we will create a new <em>Docker image</em> within the same Dockerfile, based on the <code>nginx</code> image. We then tell Docker to copy the files from the other image, that we named <em>build</em> earlier. The <code>nginx</code> image then automatically serves all files found under <code>/usr/share/nginx/html</code>.</p>
<pre><code class="language-docker">FROM nginx:1.17.0-alpine
COPY --from=build /app/dist /usr/share/nginx/html
</code></pre>
<h4 id="buildingandrunningthedockerimage">Building and running the Docker image</h4>
<p>After following all previous steps, we end up with the following Dockerfile.</p>
<pre><code class="language-docker">FROM node:10 as build

WORKDIR /app/src
COPY assets ./assets/
COPY lib ./lib/
COPY src ./src/
COPY main.lua conf.lua ./
RUN sed -i &quot;s/t.version/-- t.version/&quot; conf.lua

RUN npm install -g love.js
RUN love.js /app/src /app/dist --title &quot;Tetris&quot; --memory 67108864

FROM nginx:1.17.0-alpine
COPY --from=build /app/dist /usr/share/nginx/html
</code></pre>
<p>From here, we can build the <em>Docker image</em> with the <code>docker</code> command line tool.</p>
<pre><code class="language-shell">docker build --tag game-image .
</code></pre>
<p>Then it can be run locally, after which it is accessible at <code>localhost:3000</code>.</p>
<pre><code class="language-shell">docker run --publish 3000:80 --detach --name game game-image
</code></pre>
<p>After running, the <em>container</em> can be shut down and removed again.</p>
<pre><code class="language-shell">docker stop game
docker rm game
</code></pre>
<p>Once you're happy with the result, you will likely want to host your <em>Docker container</em> somewhere simple, like <a href="https://www.heroku.com/">Heroku</a> or <a href="https://cloud.google.com/run">Google Cloud</a>. Or if you're the DIY type, you can set up a server with <a href="https://m.do.co/c/2c4a9b1d485c">DigitalOcean</a> and use the self-hosted <a href="http://dokku.viewdocs.io/dokku/">Dokku</a> to deploy your game.</p>
<h2 id="troubleshooting">Troubleshooting</h2>
<p>If you followed the steps outlined above, you should be able to run your game in the browser with or without Docker. However, it's possible that you still run into issues. So in this section we discuss some errors that I experienced myself, but if you run into something else, leave a comment below.</p>
<h3 id="mygameisshowingluaerrors">My game is showing Lua errors</h3>
<p>The first step with Lua errors is making sure that these errors disappear when you're running your game locally with LÖVE. If they also occur that way, there is likely an <strong>unrelated bug in your code</strong> that needs to be fixed first.</p>
<p>If the Lua errors only occur in the browser, it is most likely that <strong>not all required files were included</strong>. So double check that you included all files in your <code>.love</code> file or Dockerfile. Note that these are likely to be different files than those in the example.</p>
<p>It is also important to reiterate that love.js does <strong>not work with system-installed libraries</strong>, so any libraries like <em>middleclass</em> or <em>penlight</em> need to be included as Lua files in your game folder.</p>
<p>Some errors that can indicate missing files are <strong><code>module 'xxx.yyy' not found</code></strong>, <strong><code>attempt to perform arithmetic on global 'XXX' (a nil value)</code></strong> or <strong><code>No code to run</code></strong>.</p>
<h3 id="thepageisshowingablackrectangle">The page is showing a black rectangle</h3>
<p>If the page is showing a black rectangle where the game should be, the most likely cause is also missing files.</p>
<h3 id="mygameisnotshowingonthescreen">My game is not showing on the screen</h3>
<p>If your game is not showing after loading the page, you should check the JavaScript console for errors. In most browsers you can right click the page and select <em>Inspect</em> or <em>Inspect element</em>, then navigate to <em>Console</em>. This shows all JavaScript errors.</p>
<p>The console will always show some errors about threads or audio, because these features are not working reliably in the browser. These errors can be ignored, although they might be difficult to distinguish from more important errors.</p>
<h4 id="typeerrorfailedtoexecutedecodeontextdecoder">&gt; TypeError: Failed to execute 'decode' on 'TextDecoder'</h4>
<p>This is a very non-descriptive error, but it generally happens <strong>if you forget to comment out the target version</strong> inside your <code>conf.lua</code> file. Make sure that you really comment it out or include the corresponding line in your Dockerfile.</p>
<p>The Dockerfile assumes that your config function signature is <code>function love.conf(t)</code>. But it is possible that you gave <code>t</code> another name, like <code>tbl</code> or <code>config</code>. In that case you should adjust your Dockerfile accordingly.</p>
<h3 id="myaudioisnotplayingcorrectly">My audio is not playing correctly</h3>
<p>If your audio stops playing all of a sudden, you might have forgotten to set your audio sources are set to <em>&quot;static&quot;</em>. When importing audio files with <code>love.audio.newSource</code>, make sure that the second parameter is set to &quot;static&quot; - see the example below. Note that the official LÖVE wiki recommends using &quot;stream&quot; instead of &quot;static&quot; for background music, but this advice should be disregarded when using love.js.</p>
<pre><code class="language-lua">background_music = love.audio.newSource(&quot;assets/audio/background_music.mp3&quot;, &quot;static&quot;)
</code></pre>
<p>Unfortunately audio can still be a bit inconsistent, even when using the &quot;static&quot; type. So for things like background music you might find that the looping doesn't work very well. But it usually works well enough.</p>
<h2 id="alternativeoptionlovewebbuilder">Alternative option: LoveWebBuilder</h2>
<p>The good thing about the methods described in this article is that <strong>it can be largely automated</strong>. Either by using Docker, or by adding the build process to a <code>Makefile</code>. This also means that the deployment of your game can be automated like with most other web development.</p>
<p>But maybe that is not important to you. Maybe you don't want to go through all these steps just to build your game for the browser. Or maybe you don't plan on repeating this deployment, so you don't value the automation.</p>
<p><img src="https://kalis.me/content/images/2020/04/love-web-builder.png" alt="Building love2d games for the web with love.js and Docker"></p>
<p>In that case, there is another tool that is built on top of love.js, called <a href="https://schellingb.github.io/LoveWebBuilder">LoveWebBuilder</a> by <a href="https://twitter.com/B_Schelling">Bernhard Schelling</a>. It still uses love.js for the Lua-to-JavaScript conversion, but LoveWebBuilder is available as a simple to use web application. You just need to select a <code>.love</code> file from your computer and enter some information. The rest is all handled by LoveWebBuilder, making it very accessible for one-time deployments.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Love2d games can be converted to browser games with love.js as long as some of the prerequisites are met. This build process can be automated with Docker by creating a Dockerfile with all build steps. There are some common issues that you could run into, which can usually be solved with the steps outlined above. An alternative to love.js is LoveWebBuilder, which can be used if you prefer to use a simple web-based UI.</p>
<p>Let me know in the comments below if you've published any love2d games on the web so I can check them out. Also let me know whether you followed this guide, used LoveWebBuilder or used a different build process altogether! And don't forget to share this article with your gamedev friends on Facebook or Twitter!</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Smart contracts on Ethereum, Bitcoin and Bitcoin Cash]]></title><description><![CDATA[Ethereum was the first platform with Turing-complete smart contracts, but it was already possible to create simple contracts on Bitcoin, and Bitcoin Cash has recently been improving its smart contract capabilities.]]></description><link>https://kalis.me/smart-contracts-eth-btc-bch/</link><guid isPermaLink="false">5d7ddfb708611b0426c0ac08</guid><category><![CDATA[crypto]]></category><category><![CDATA[smart contracts]]></category><category><![CDATA[ethereum]]></category><category><![CDATA[bitcoin]]></category><category><![CDATA[bitcoin cash]]></category><dc:creator><![CDATA[Rosco Kalis]]></dc:creator><pubDate>Thu, 12 Dec 2019 13:55:43 GMT</pubDate><media:content url="https://kalis.me/content/images/2019/12/eth-btc-bch-1.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://kalis.me/content/images/2019/12/eth-btc-bch-1.jpg" alt="Smart contracts on Ethereum, Bitcoin and Bitcoin Cash"><p>Ethereum was the first platform to enable <strong>Turing-complete smart contracts</strong> back in 2015. It is still the most popular platform for smart contracts, even as other smart contract platforms have emerged, like EOS or Tezos. And although Ethereum was the first platform with Turing-complete smart contracts, it was already possible to create rudimentary contracts on Bitcoin using a language called <strong>Bitcoin Script</strong>. And Bitcoin Cash has recently been <strong>improving its smart contract capabilities</strong>. While not as advanced as Ethereum's, they allow for useful on-chain contracts with unique benefits.</p>
<p>This article focuses on the unique differences between smart contracts on these three platforms. Since the focus is on the smart contract and scripting functionality, <strong>we don't go into the platforms' fundamentals or blockchain in general</strong>. This article is also specifically about on-chain smart contracts. Several second layer smart contract solutions exist, such as <a href="https://www.rsk.co/">RSK</a>. These solutions are definitely worth discussing, but are better suited for an article of their own.</p>
<h2 id="ethereumstatefulturingcomplete">Ethereum: Stateful &amp; Turing-complete</h2>
<p>Ethereum is the biggest smart contract platform in the world, and with good reason. Smart contracts in Ethereum use the <strong>Ethereum Virtual Machine (EVM)</strong>, which is a Turing-complete Virtual Machine. This means that the EVM is able to compute anything, given enough resources. This is conceptually similar to many other general-purpose platforms, such as the JVM, which is used in the execution of Java programs.</p>
<h3 id="theevmexplained">The EVM explained</h3>
<p>A big difference between these general-purpose platforms and Ethereum's EVM is that Ethereum's smart contract code is <strong>executed by all Ethereum nodes</strong> to verify transaction validity. To compensate mining nodes for the execution of this code, all EVM opcodes have an associated <strong>gas cost</strong>, referenced in the image below.</p>
<img src="https://kalis.me/content/images/2019/12/evm-fee-schedule.png" width="90%" alt="Smart contracts on Ethereum, Bitcoin and Bitcoin Cash">
<figcaption style="margin: -25px 0 15px 0;">
    EVM fee structure (<a href="https://ethereum.github.io/yellowpaper/paper.pdf">Source</a>)
</figcaption>
<p>Every transaction uses an amount of gas, depending on the opcodes used. Gas is paid for with Ethereum's native currency, Ether. To limit the amount of computation these nodes have to do per block, there is a limit on the amount of gas that can be used in a single block, called the <strong>block gas limit</strong>.</p>
<p>During the execution of smart contract functions, contracts can store and access the necessary data. This data can live in different locations depending on its use. First is the <strong>stack</strong>, which holds the values used in computations. Only the top 16 items on the stack can be easily accessed, so it's not suitable for longer term storage. The use of a stack for computation is shown below.</p>
<img src="https://kalis.me/content/images/2019/12/stack-based-computation.png" width="60%" alt="Smart contracts on Ethereum, Bitcoin and Bitcoin Cash">
<figcaption style="margin: -25px 0 15px 0;">
    Stack-based computation (<a href="https://fsharpforfunandprofit.com/posts/stack-based-calculator/">Source</a>)
</figcaption>
<p>To complement the stack, the <strong>contract memory</strong> data location is used to store, retrieve and pass data within the current contract execution. The values can be retrieved from memory to be used on the stack for computation and the results can be stored back in memory. These values only persist during the current execution; <strong>at the end of the execution the memory and stack are wiped</strong>.</p>
<p>The final data location is <strong>contract storage</strong>, which is used to persist data across contract executions. Persistent variables, like token balances, are stored in contract storage. To achieve data persistence, the contract storage is <strong>stored on every Ethereum node</strong>. So memory is analogous to RAM, while storage is analogous to hard drive storage or a persistent database.</p>
<p>More information about the inner workings of the EVM can be found in <a href="https://medium.com/mycrypto/the-ethereum-virtual-machine-how-does-it-work-9abac2b7c9e">this excellent article by MyCrypto</a>.</p>
<h3 id="writingsmartcontracts">Writing smart contracts</h3>
<p>While all smart contracts use the EVM, most of them are not written by hand using EVM bytecode, like most JVM bytecode is not written by hand. Instead there are several <strong>high-level languages</strong> that can be used to write smart contracts in Ethereum. The most popular language for this is <a href="https://solidity.readthedocs.io/en/latest/"><strong>Solidity</strong></a>, but <a href="https://vyper.readthedocs.io/en/latest/">Vyper</a> also sees some usage. An example of a very simple Solidity smart contract is included below.</p>
<pre><code class="language-solidity">pragma solidity ^0.5.0;

contract SimpleStorage {
  uint storedData;

  function set(uint x) public {
    storedData = x;
  }

  function get() public view returns (uint) {
    return storedData;
  }
}
</code></pre>
<h3 id="interactingwithsmartcontracts">Interacting with smart contracts</h3>
<p>Smart contracts in Ethereum exist as bytecode on the Ethereum network. This means that Solidity code gets compiled to bytecode, which is then deployed to the network by <strong>sending a deployment transaction</strong>. This is a special kind of transaction without any recipient but with the bytecode as the transaction data.</p>
<p>These deployed contracts exist only as blobs of EVM bytecode on the network, which are nearly impossible to use on their own. To interface with them, an <strong>Application Binary Interface (ABI)</strong> needs to be provided, which includes a list of all public functions and their parameters. To grant more insight into these contracts, there are services like <a href="https://etherscan.io/">Etherscan</a> that allow you to <a href="https://kalis.me/verify-truffle-smart-contracts-etherscan/">verify contract source code</a> so users can inspect the code before using the contracts.</p>
<p>All smart contracts can be <strong>accessed directly</strong> by connecting to an Ethereum node and using its JSON-RPC interface. But many smart contracts are instead accessed through a frontend application that connects to a node and manages the ABI. This can be done with one of many different Ethereum SDKs like web3js or ethers.js, which call the JSON-RPC under the hood. <strong>This offers a better experience for contract users</strong>, as the most difficult parts are abstracted away.</p>
<p>Smart contracts can be interacted with in two different ways: <strong>through calls and through transactions</strong>. A call is a local invocation of a contract function, and it does not broadcast anything to the blockchain. Because of this, calls are read-only, can not make any changes to the contract state and do not incur any fees.</p>
<p>On the other hand, transactions are write operations that do get broadcasted to the network, are included on the blockchain and do incur a miner fee. To take ERC20 tokens as an example, token balances are retrieved with a call, while tokens are transferred with a transaction.</p>
<h3 id="interactionbetweensmartcontracts">Interaction between smart contracts</h3>
<p>These properties of Ethereum - a Turing-complete VM and persistent storage - allow you to create any kind of <strong>decentralised applications</strong> running on-chain. A great example of this is the DeFi ecosystem, with applications such as <a href="https://makerdao.com/en/">Maker</a>, <a href="https://uniswap.io/">Uniswap</a> and <a href="https://compound.finance/">Compound</a>, as well as the ERC20 and ERC721 token standards. These applications allow for complex functionality with an unlimited number of dynamic participants.</p>
<img src="https://kalis.me/content/images/2019/12/defi-interop-rdai.png" width="20%" alt="Smart contracts on Ethereum, Bitcoin and Bitcoin Cash">
<figcaption style="margin: -25px 0 15px 0;">
    DeFi interoperability in rDai (<a href="https://rdai.money/">Source</a>)
</figcaption>
<p>On top of that, smart contracts themselves can be participants in other smart contracts, allowing for strong <strong>integration</strong> and <strong>composition</strong> between these contracts. An example is a smart contract holding a token balance and lending them out on Compound or exchanging them with Uniswap. Another example is using <a href="https://rdai.money/">rDai</a> to automatically invest DAI and contribute the accrued interest to charity.</p>
<p>While smart contracts can interact with each other, <strong>every on-chain transaction needs to be originated from an external account</strong>. So contract-to-contract interactions still have to be triggered through an initial transaction by a user. After this initial trigger, contract-to-contract interactions are similar to direct interactions.</p>
<p>In other words, it's possible for contracts to call read-only functions of other smart contract or trigger write operations. Since only external accounts can trigger transactions, write operations between contracts are referred to as <em>internal transactions</em> or <em>message calls</em> to make the distinction with user-initiated transactions.</p>
<h2 id="bitcoinstatelesssimple">Bitcoin: Stateless &amp; simple</h2>
<p>All Bitcoin transactions, including regular transfers, are powered by a stack-based programming language called <strong>Bitcoin Script</strong>. And while the EVM has been designed for Turing-complete and integrated smart contracts, Bitcoin Script is <strong>intentionally more limited</strong> and works in a fundamentally different way.</p>
<h3 id="bitcoinscriptexplained">Bitcoin Script explained</h3>
<p>Like the EVM, Bitcoin Script uses a stack to hold values and perform computations on these values. But unlike the EVM, <strong>the stack is the only data location available</strong> in Bitcoin Script. This means that it's difficult to store multiple values to use later in the contract execution. But more importantly, this means that it is impossible to store and modify values that persist across contract executions.</p>
<p>This is the biggest difference between Ethereum's smart contracts and Bitcoin's. <strong>Ethereum's model is stateful, while Bitcoin is stateless</strong>. Ethereum can be considered analogous to the common imperative mutable-data programming paradigm, while Bitcoin is analogous to the functional immutable-data paradigm.</p>
<p>Bitcoin's model allows <strong>transactions to be verified independently and much more efficiently</strong>, which makes it easier to parallelise and shard transactions. But without any mutable data storage, it is more difficult to create the complex smart contracts that Ethereum allows. ERC20 contracts, for example, have to keep track of token balances and change them.</p>
<p>Besides these differences in state, there are other things limiting complexity in Bitcoin's smart contracts. Notably, Bitcoin Script lacks support for some arithmetic functions and any form of looping or recursion. Contracts also have an effective size limit of <strong>520 bytes</strong> and can contain <strong>201 opcodes</strong> at most.</p>
<p>Most smart contracts on Bitcoin fall in a few categories of simple contracts. Examples are multisig contracts that can be spent by multiple participants, or Hashed Timelock Contracts (HTLCs) that can be spent by revealing a secret or reclaimed after time has passed. And because these contracts are very simple, most of the value is gained from <strong>combining different contracts with additional off-chain application logic</strong>. That way simple contracts can be combined to create complex solutions such as the <a href="https://cointelegraph.com/lightning-network-101/what-is-lightning-network-and-how-it-works">Lightning Network</a> or <a href="https://atomic.loans/">Atomic Loans</a>.</p>
<h3 id="bitcointransactionsexplained">Bitcoin transactions explained</h3>
<p>Bitcoin transactions are created using indivisable <em>chunks</em> of Bitcoin, called <em>transaction outputs</em>. When these outputs are available, they are called <em>Unspent Transaction Outputs (UTXOs)</em>. <strong>UTXOs are locked using a locking script</strong> (or <code>scriptPubKey</code>) that specifies the conditions to spend the output. When attempting to spend a UTXO, an unlocking script (or <code>scriptSig</code>) is provided. These scripts are then executed together; the transaction is only valid if the scripts execute without errors and the resulting value is TRUE.</p>
<p>As with the EVM, all Bitcoin nodes execute these scripts to validate the transactions, but Bitcoin lacks the notion of a gas cost, so instead <strong>fees are paid to the miner per byte of transaction data</strong>. To limit the work that the nodes have to do, there are also limits in place on some of the more intensive script operations.</p>
<h3 id="interactingwithsmartcontracts">Interacting with smart contracts</h3>
<p>Smart contracts in Bitcoin are written using the <strong>Pay-to-Script-Hash (P2SH)</strong> pattern. The locking script in the P2SH pattern contains a script hash, and requires an unlocking script that provides the full script (called the <strong>redeem script</strong>) as well as the unlocking script for this redeem script. The image below shows this pattern.</p>
<img src="https://kalis.me/content/images/2019/12/p2sh.png" width="60%" alt="Smart contracts on Ethereum, Bitcoin and Bitcoin Cash">
<figcaption style="margin: -25px 0 15px 0;">
    Pay-to-Script-Hash (P2SH) (<a href="https://learnmeabitcoin.com/guide/p2sh">Source</a>)
</figcaption>
<p>The Bitcoin node software validates these smart contract transactions in two phases. First, the redeem script is hashed and checked against the hash in the locking script. If they match, the unlocking script is used to unlock the redeem script <strong>as if the redeem script was the initial locking script</strong>.</p>
<p>Because only a hash of the bytecode is stored on-chain, <strong>the full bytecode has to be stored off-chain</strong>, and included in the unlocking script of any contract execution. Because of this, &quot;deployments&quot; of smart contracts are free, but later contract executions are more expensive. Conversely, initial deployments in Ethereum are relatively expensive, while later contract executions are cheaper.</p>
<p>These differences in deployment <strong>encourage different kinds of smart contracts</strong>. A good example is <a href="https://localcryptos.com/r/rkalis">LocalCryptos</a>, which supports non-custodial local trading for both Ethereum and Bitcoin. For Ethereum it uses a big contract that keeps track of all trades, while for Bitcoin it creates individual contracts for every trade.</p>
<h3 id="writingsmartcontracts">Writing smart contracts</h3>
<p>While Ethereum has multiple high-level languages that compile to EVM bytecode, there is less of a focus on this with Bitcoin. Although the systems that can be built with them are complex, Bitcoin contracts themselves are usually quite simple. So there is <strong>less of a need to abstract away</strong> the underlying system. And because of Bitcoin contracts' size limits and high costs per added bytes, it is <strong>important to keep contracts as small as possible</strong>.</p>
<p>While these kinds of high-level languages are less important in Bitcoin, they do exist. The most elaborate high-level language for Bitcoin is <a href="https://github.com/ivy-lang/ivy-bitcoin">Ivy</a>, which was created in 2017 by <a href="https://twitter.com/danrobinson">Dan Robinson</a>. An example smart contract written in Ivy is included below. The contract can be used to send money that can be reclaimed by the sender if the recipient doesn't spend it timely.</p>
<pre><code class="language-javascript">contract TransferWithTimeout(
  sender: PublicKey,
  recipient: PublicKey,
  timeout: Time,
  val: Value
) {
  clause transfer(senderSig: Signature, recipientSig: Signature) {
    verify checkSig(sender, senderSig)
    verify checkSig(recipient, recipientSig)
    unlock val
  }
  clause timeout(senderSig: Signature) {
    verify checkSig(sender, senderSig)
    verify after(timeout)
    unlock val
  }
}
</code></pre>
<p>More recently several researchers at Blockstream released <a href="http://bitcoin.sipa.be/miniscript/">Miniscript</a>, which is a language focused on analysis and composability of smart contracts, rather than abstracting away the underlying system. This seems to be the right path, given the fact that Bitcoin contracts tend to lack the complexity that needs more abstraction.</p>
<h2 id="bitcoincashcombiningfeatures">Bitcoin Cash: Combining features</h2>
<p>On one hand there is Ethereum, which is able to create many powerful and useful smart contracts that live completely on-chain. At the same time it presents scaling issues due to its stateful nature. On the other hand, Bitcoin's stateless model for smart contracts allows for independent, simple verification of smart contract transactions. But its scripting system limits the usefulness of its contracts.</p>
<p>Bitcoin Cash and Bitcoin share the same history until they forked, so their underlying scripting systems are the same in functionality and <strong>Bitcoin Cash benefits from the same upsides</strong>. Since then, a part of the Bitcoin Cash community has recognised the demand for more useful smart contracts. Bitcoin Cash has enabled new functionality, <strong>making its smart contracts more useful</strong>, while keeping the essential properties that allow for Bitcoin's stateless verification.</p>
<h3 id="upgradedfunctionality">Upgraded functionality</h3>
<p>To understand the possibilities of smart contracts on Bitcoin Cash we need to look at its <strong>biannual network upgrades</strong>. These network upgrades are performed every year in May and November since the original hard fork in 2017. We specifically discuss the changes to the Bitcoin Script engine, although several other improvements have been made, such as Schnorr signatures.</p>
<p>Early on in Bitcoin, several opcodes were disabled due to issues that made them unsafe to use. Within the first year after the Bitcoin Cash fork, the developers of the Bitcoin-ABC node addressed these issues and <a href="https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/may-2018-reenabled-opcodes.md"><strong>reintroduced the opcodes with slightly amended functionality</strong></a>, shown in the image below. Most importantly, this update enables <strong>encoding and decoding of structured data</strong> within Bitcoin Script.</p>
<img src="https://kalis.me/content/images/2019/12/may2018-reenabled-opcodes.png" width="80%" alt="Smart contracts on Ethereum, Bitcoin and Bitcoin Cash">
<figcaption style="margin: -25px 0 15px 0;">
    New opcodes re-enabled in May 2018 (<a href="https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/may-2018-reenabled-opcodes.md">Source</a>)
</figcaption>
<p>Half a year after this, another new opcode was released in the network upgrade of November 2018. The update included <code>OP_CHECKDATASIG</code>, which allows you to <strong>verify a signature for any message inside Bitcoin Script</strong>. If we combine all scripting updates that were introduced in 2018, these can be used to bring novel and useful smart contract functionality into Bitcoin Cash.</p>
<h3 id="writingsmartcontracts">Writing smart contracts</h3>
<p>The new functionality of Bitcoin Cash adds significant complexity to its smart contracts over the original Bitcoin Script. This makes it more important to have an ecosystem that provides <strong>higher levels of abstraction</strong> through high-level languages, SDKs and tooling.</p>
<p>The two big projects working on this right now are <a href="https://spedn.readthedocs.io/en/latest/"><strong>Spedn</strong></a>, which was created by the pseudonymous <a href="https://twitter.com/tendo_pein_sama">Tendo Pein</a>, and <a href="https://cashscript.org/"><strong>CashScript</strong></a>, which was created by me and is syntactically inspired by Ethereum's Solidity. These tools make it easier to work with smart contracts in Bitcoin Cash, although they are still in development. We use snippets of CashScript code to illustrate functionality in the sections below.</p>
<h3 id="oracles">Oracles</h3>
<p>When the past updates to Bitcoin Script are combined, they allow you to <strong>bring external data into smart contracts</strong> on Bitcoin Cash through trusted oracles. Structured data can be encoded into a byte array, and signed by an oracle provider. Then the smart contract can verify the signature and decode the structured data.</p>
<p>An example of this can be inspected in the <a href="https://github.com/Bitcoin-com/cashscript/blob/master/examples/hodl_vault.cash">HODL Vault</a> example contract below. This contract enforces HODLing until a certain BCH/USD price has been reached. The required BCH/USD price feed is published by an oracle provider and passed into the contract by the user. To increase decentralisation, a smart contract can be set up to use <strong>several data sources</strong>, rather than trusting a single centralised service.</p>
<pre><code class="language-solidity">pragma cashscript ^0.2.0;

contract HodlVault(pubkey ownerPk, pubkey oraclePk, int minBlock, int priceTarget) {
    function spend(sig ownerSig, datasig oracleSig, bytes oracleMessage) {
        // Decode message: { blockheight, price }
        int blockHeight = int(oracleMessage.split(4)[0]);
        int price = int(oracleMessage.split(4)[1]);

        // Check that message's blockHeight is after minBlock and not in the future
        require(blockHeight &gt;= minBlock);
        require(tx.time &gt;= blockHeight);

        // Check that current price is at least priceTarget
        require(price &gt;= priceTarget);

        // Handle necessary signature checks
        require(checkDataSig(oracleSig, oracleMessage, oraclePk));
        require(checkSig(ownerSig, ownerPk));
    }
}
</code></pre>
<h3 id="covenants">Covenants</h3>
<p>The second big use case is a technique called covenants, which derives its name from a term used in property law to restrict an object's use. In the case of Bitcoin Cash, <strong>it restricts the use of money in a smart contract</strong>. So while smart contracts in Bitcoin can only authorise the general spending of money, Bitcoin Cash contracts are able to put constraints on the amount of money that can be spent or who the recipients can be, among other constraints.</p>
<p>When transferring Bitcoin, the sender has to provide a signature to authorise the transaction. To generate this signature, the sender signs a hash representation of the transaction. This hash is called the <strong>sighash</strong>, while the actual transaction data is contained in the <strong>sighash preimage</strong>.</p>
<p>By using <code>OP_CHECKSIG</code> and <code>OP_CHECKDATASIG</code> with the same signature, we can <strong>gain access to the sighash data</strong>. The data format can be inspected in the <a href="https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/replay-protected-sighash.md#digest-algorithm">specification</a>, and is included in the image below. An important field is <code>scriptCode</code>, which contains the bytecode of the smart contract itself. Another is <code>hashOutputs</code> that allows you to <strong>enforce the outputs of a transaction</strong>.</p>
<img src="https://kalis.me/content/images/2019/12/sighash-data-format.png" width="60%" alt="Smart contracts on Ethereum, Bitcoin and Bitcoin Cash">
<figcaption style="margin: -25px 0 15px 0;">
    Bitcoin Cash sighash data format (<a href="https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/replay-protected-sighash.md">Source</a>)
</figcaption>
<p>While it is good to know how covenants work on a technical level, CashScript <strong>has abstracted away most of the complexity</strong> associated with covenants. When writing smart contracts with CashScript these fields are readily available without going through the steps to verify and decode the sighash preimage manually.</p>
<p>The first smart contract to use covenants was <a href="https://github.com/KarolTrzeszczkowski/Electron-Cash-Last-Will-Plugin">Licho's Last Will</a>, a smart contract that allows you to put a dead man's switch on your holdings. The contract defines three different functions. The first allows an inheritor to claim the funds after 180 days. The second allows the owner's cold key to spend the money in any way. The third allows the owner's hot key to refresh the 180 day duration by <strong>enforcing that the full contract balance is sent back to the contract</strong>.</p>
<p>A CashScript version of Last Will is included below, but the original version was written with Spedn and can be inspected <a href="https://github.com/KarolTrzeszczkowski/Electron-Cash-Last-Will-Plugin/blob/master/LastWill.spedn">here</a>.</p>
<pre><code class="language-solidity">pragma cashscript ^0.3.0;

contract LastWill(bytes20 inheritor, bytes20 cold, bytes20 hot) {
    function inherit(pubkey pk, sig s) {
        require(tx.age &gt;= 180 days);
        require(hash160(pk) == inheritor);
        require(checkSig(s, pk));
    }

    function cold(pubkey pk, sig s) {
        require(hash160(pk) == cold);
        require(checkSig(s, pk));
    }

    function refresh(pubkey pk, sig s) {
        require(hash160(pk) == hot);
        require(checkSig(s, pk));

        // Construct output
        int minerFee = 1000;
        bytes8 amount = bytes8(int(bytes(tx.value)) - minerFee);
        bytes32 output = new OutputP2SH(amount, hash160(tx.bytecode)

        // Check that output matches preimage
        require(hash256(output) == tx.hashOutputs);
    }
}
</code></pre>
<p>While this is one of the simpler examples of a covenant contract, it's possible to create much more interesting covenants. People have used covenants to create <a href="https://github.com/KarolTrzeszczkowski/Mecenas-recurring-payment-EC-plugin">Mecenas</a>, a recurring payment system, and <a href="http://be.cash/">Be.cash</a>, a system for payer-offline payment cards. More recently we have even seen <a href="https://anyhedge.com/">AnyHedge</a>, a <strong>decentralised derivatives platform</strong>. So while Bitcoin Cash uses a different paradigm than Ethereum, it can support <strong>complex DeFi projects</strong>.</p>
<p>Covenants were first proposed in a paper titled <a href="https://fc16.ifca.ai/bitcoin/papers/MES16.pdf">Bitcoin Covenants</a>, which required a new opcode, <code>OP_CHECKOUTPUTVERIFY</code>. Other proposals followed, such as <a href="https://github.com/bitcoin/bips/blob/master/bip-0119.mediawiki">BIP-119</a> or <a href="https://fc17.ifca.ai/bitcoin/papers/bitcoin17-final28.pdf"><code>OP_CHECKSIGFROMSTACK</code></a>. The latter is very similar to Bitcoin Cash's implementation of covenants. More practical information on covenants can be found in <a href="https://read.cash/@pein/bch-covenants-with-spedn-c1170a02">Tendo Pein's article on read.cash</a>.</p>
<h3 id="simulatedstate">Simulated state</h3>
<p>For the Last Will contract it can be valuable to change the inheritor when the contract is already in place. Since we can enforce sending to the current contract by looking at its bytecode, we can enforce sending to a slightly different contract by <strong>slightly changing this bytecode</strong>. Doing so, we create a function that sends the entire contract's balance to a contract with the <strong>exact same bytecode, but with a different inheritor</strong>.</p>
<p>We can only do this with constructor parameters of a known size (e.g. 20 bytes). The constructor parameters are always the first data of the contract's bytecode, which is how we are able to easily <strong>replace the old data with new data</strong>. The <code>inheritor</code> is the first constructor parameter and it has a size of 20 bytes, so we are able to apply this technique, as can be seen below.</p>
<pre><code class="language-solidity">    function changeInheritor(pubkey pk, sig s, bytes20 newInheritor) {
        require(hash160(pk) == hot);
        require(checkSig(s, pk));
        
        // Cut out old inheritor (PUSH1 0x14 &lt;inheritor&gt;) 
        // Insert new inheritor (PUSH1 0x14 &lt;newInheritor&gt;)
        bytes newContract = 0x4c14 + newInheritor + tx.bytecode.split(22)[1];

        // Construct output
        int minerFee = 1000;
        bytes8 amount = bytes8(int(bytes(tx.value)) - minerFee);
        bytes32 output = new OutputP2SH(amount, hash160(newContract));

        // Check that output matches preimage output
        require(hash256(output) == tx.hashOutputs);
    }
</code></pre>
<p>By using this technique, it's possible to <strong>change some variables in a contract</strong> while keeping the same rules of the contract. We refer to this as &quot;simulating&quot; state, since it offers some of the benefits of contract state, without some of the drawbacks of a stateful system. There is always a trade-off, so this method has different drawbacks.</p>
<p>The main issue is that we're not actually changing any variables in the original contract, since this is impossible due to Bitcoin Cash's statelessness. Instead, <strong>a new contract is created (with a new address)</strong> and the full balance can be transferred to this new contract. This causes UX problems due to the new address, but these can be mitigated by having a good application layer abstraction over these smart contracts.</p>
<p>Replacing these variables inside the bytecode can feel quite hacky, especially when trying to replace variables that are deeper inside the bytecode. <strong>This functionality should be abstracted away</strong> in high-level languages, so that the new bytecode is automatically generated by providing the new parameters. That kind of abstraction could look like this:</p>
<pre><code class="language-solidity">bytes32 output = new OutputP2SH(amount, hash160(new LastWill(newInheritor, cold, hot)));
</code></pre>
<p>Many abstractions come at the cost of larger contracts, which results in higher fees. Because transaction fees are generally quite low on Bitcoin Cash, this is not a deterrent. But Bitcoin Cash has the same size  limits as Bitcoin, so these abstractions do make it more difficult to stay within the limits. This results in <strong>many developers having to hand-optimise the compiled bytecode</strong>.</p>
<h2 id="conclusions">Conclusions</h2>
<p>Every cryptocurrency has <strong>trade-offs in its smart contract systems</strong>. Ethereum is the most used smart contract platform with the most extensive functionality. Bitcoin offers a more rudimentary version of smart contracts through its stateless scripting system. This stateless system is more efficient to validate, but offers less functionality. Bitcoin Cash builds on the same foundations as Bitcoin, but has added new functionality. This attempts to achieve a compromise between efficient validation and useful smart contracts. In the end, <strong>all are building towards similar goals</strong>.</p>
<h2 id="lookingintothefuture">Looking into the future</h2>
<p>This article is written in Q4 of 2019, and reflects the current state of the platforms. Cryptocurrency is a fast-paced field, so the people involved with these platforms are always working on improvements that may change the outlook of the ecosystem.</p>
<p>Ethereum is working on a major Ethereum 2.0 release, with its <a href="https://docs.ethhub.io/ethereum-roadmap/ethereum-2.0/eth-2.0-phases/#phase-0-beacon-chain">phase 0 planned to go live in Q1 2020</a>. <strong>Ethereum 2.0 aims to address the performance of Ethereum 1.x</strong> by moving to Proof-of-Stake, implementing sharding, and other improvements. The full roadmap for ETH2.0 is not set in stone yet, and it will likely take several years to roll out. But if this major update is able to to achieve its goals, this can present a <strong>strong case for Ethereum</strong>.</p>
<p>Smart contract research in Bitcoin is focused on further improving efficiency and privacy of smart contracts. This includes solutions like <a href="https://bitcoinmagazine.com/articles/taproot-coming-what-it-and-how-it-will-benefit-bitcoin">Taproot</a> and <a href="https://bitcoinmagazine.com/articles/scriptless-scripts-how-bitcoin-can-support-smart-contracts-without-smart-contracts">scriptless scripts</a>. At the same time Bitcoin Cash has enabled additional smart contract functionality and is focused on making these changes more accessible.</p>
<hr>
<p><strong>Disclaimer</strong>: I am the author of CashScript, one of the projects working on Bitcoin Cash smart contracts. I have also worked on and contributed to open source Ethereum projects, and currently maintain several Ethereum libraries. The goal of this article is not to advocate for any of the discussed platforms or technologies, but rather to provide a comprehensive comparison for anyone interested in working with smart contracts on Ethereum, Bitcoin and Bitcoin Cash.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Creating Truffle plugins]]></title><description><![CDATA[One of the final Truffle updates of 2018 included the addition of Truffle plugins. But how do we create Truffle plugins, and what are their possibilities?]]></description><link>https://kalis.me/creating-truffle-plugins/</link><guid isPermaLink="false">5d45221884379b2799d01a6d</guid><category><![CDATA[ethereum]]></category><category><![CDATA[crypto]]></category><category><![CDATA[truffle]]></category><dc:creator><![CDATA[Rosco Kalis]]></dc:creator><pubDate>Sat, 03 Aug 2019 23:30:00 GMT</pubDate><media:content url="https://kalis.me/content/images/2019/08/creating-truffle-plugins-how-to.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://kalis.me/content/images/2019/08/creating-truffle-plugins-how-to.jpg" alt="Creating Truffle plugins"><p>One of the final Truffle updates of 2018 included the addition of Truffle plugins - the possibility to extend Truffle's functionality with new Truffle CLI commands. While it has been out for over half a year now, there has not been a lot of innovation in the form of new Truffle plugins. In fact, the <a href="https://github.com/rkalis/awesome-truffle-plugins">list of awesome Truffle plugins</a> currently counts just two plugins: <a href="https://github.com/rkalis/truffle-plugin-verify">truffle-plugin-verify</a> and <a href="https://github.com/ConsenSys/truffle-security">truffle-security</a>.</p>
<p>Needless to say, it is time for more useful plugins to be developed. But the remaining questions are where do we start, and what even are the possibilities of these Truffle plugins? In this article we discuss how to create a Truffle plugin, and we discuss what these plugins can be used for.</p>
<h2 id="usingtruffleplugins">Using Truffle plugins</h2>
<p>Before being able to create Truffle plugins we need to understand how to use them and how to integrate one into an existing Truffle project. Luckily this process is very simple, as Truffle plugins are simply NPM packages that serve a very specific function - extending Truffle. After setting up a Truffle project, any plugin can be integrated into this project with a few simple steps.</p>
<p>First, all Truffle plugins can be installed into a project with NPM.</p>
<pre><code class="language-bash">npm install truffle-plugin-verify
</code></pre>
<p>Next, these plugins need to be enabled by adding them to your <code>truffle-config.js</code> or <code>truffle.js</code> file under the <code>plugins</code> field.</p>
<pre><code class="language-js">module.exports = {
  /* ... rest of truffle-config */

  plugins: [
    'truffle-plugin-verify'
  ]
}
</code></pre>
<p>Any plugin should be usable with just these two steps, but some plugins might require additional steps, such as adding API keys or other additional parameters. So always be sure to check the README of a specific plugin.</p>
<pre><code class="language-js">module.exports = {
  /* ... rest of truffle-config */

  api_keys: {
    etherscan: 'MY_API_KEY'
  }
}
</code></pre>
<p>When a plugin is fully installed and enabled, it can be used from the Truffle CLI.</p>
<pre><code class="language-bash">truffle run verify Casino --network rinkeby
</code></pre>
<h2 id="howdotrufflepluginswork">How do Truffle plugins work?</h2>
<p>All Truffle plugins are structured in the same way. They include a <code>truffle-plugin.json</code> file that maps one or more CLI commands to their corresponding JS files.</p>
<pre><code class="language-json">{
    &quot;commands&quot;: {
        &quot;hello&quot;: &quot;hello.js&quot;
    }
}
</code></pre>
<p>These JS files then have a single function as their default export. This function has a <code>config</code> object as a parameter that is used inside the function to implement the plugin's functionality.</p>
<pre><code class="language-js">module.exports = async (config) =&gt; {
  if (config.help) {
    console.log(`Usage: truffle run hello [name]`);
    done(null, [], []);    
    return;
  }

  let name = config._.length &gt; 1 ? config._[1] : 'World!';
  console.log(`Hello, ${name}`);
}
</code></pre>
<h3 id="theconfigobject">The config object</h3>
<p>The config object contains all the information you will ever need to start creating useful Truffle CLI plugins. The most important fields in this object are outlined below.</p>
<pre><code class="language-ts">Config {
  // Information about the CLI
  truffle_directory: string, // Truffle executable location
  working_directory: string, // CWD
  _: string[], // Raw positional command line arguments

  // Any additional command line flags / arguments
  myString: string // --myString hello
  myint: number // --myInt 5

  // Information included in the truffle-config.js file
  networks: { rinkeby: object, ropsten: object },
  compilers: { solc: object, vyper: {} },
  plugins: string[],
  api_keys: { etherscan: string },
  /* ... any other fields defined in truffle-config.js */

  // Network config
  network: string, // --network rinkeby|ropsten|etc
  network_id: number // Current network id
  network_config: object // Current network config (from truffle-config.js)

  // Directory information
  contracts_directory: string, // ./contracts
  build_directory: string, // ./build
  contracts_build_directory: string, // ./build/contracts
  migrations_directory: string, // ./migrations
  migrations_file_extension_regexp: RegExp,
  test_directory: string, // ./test
  test_file_extension_regexp: RegExp,
}
</code></pre>
<p>The first thing we see in this config object is information about the CLI, including working directory and command line arguments. The object also includes any passed in command line flags, which gives the truffle plugin any functionality you would expect from other CLI-based tools.</p>
<p>The next thing that's included is all the information included in your <code>truffle-config.js</code> or <code>truffle.js</code> file. This includes things like network configuration and compiler settings, but also any other user-defined fields, such as the <code>api_keys</code> field that is used in truffle-plugin-verify. Besides the raw information, there's some additional Truffle magic included in the additional network information that is added based on the <code>--network</code> parameter passed into the CLI.</p>
<p>The final information in the config object is directory information. This is what really enables interesting functionality. It gives access to all migrations files and test files, which allows you to run these migrations or tests inside your plugin, or to inspect their source code. But what really opens doors are the <code>contracts_directory</code> and <code>contracts_build_directory</code> strings.</p>
<h3 id="retrievingcontractinformationusingcontracts">Retrieving contract information &amp; using contracts</h3>
<p>The <code>contracts_directory</code> field gives us access to all the contract source files. This can be used for things like code flattening, linting, compiling, static analysis, or any other kind of source code analysis. But for most use cases, this <code>contracts_directory</code> field is not even necessary, as the <code>contracts_build_directory</code> grants access to all Truffle artifacts produced by Truffle's compile process. These artifacts include the contract's source paths, pointing to contract's source files as well.</p>
<p>The Truffle artifacts grant access to much more though, as they include all the information about the contracts, including bytecode, ABI, source code, and even the contract's Abstract Syntax Tree (AST), allowing you to add some compiler-related operations to your Truffle plugins.</p>
<p>Furthermore, artifacts include the addresses of contract instances that have been deployed using Truffle's migrate functionality. This allows you to analyse these deployed instances by looking at their transaction history, emitted events, or gather information on these contracts through public APIs such as Etherscan's.</p>
<p>But it doesn't stop there, since the artifact also includes the contract's ABI, allowing the plugin to send transactions or call functions on this contract. And because the config object also includes the network configuration, the plugin can even use the contract on behalf of the user's account when they are using something like <a href="https://www.npmjs.com/package/truffle-hdwallet-provider">truffle-hdwallet-provider</a> or <a href="https://www.npmjs.com/package/truffle-privatekey-provider">truffle-privatekey-provider</a>.</p>
<h2 id="creatingatruffleplugin">Creating a Truffle plugin</h2>
<p>Now that we know how a Truffle plugin works technically, what information it gets, and what can be derived from this information, it is time to develop a useful Truffle plugin. In this article we walk through the creation of a simple, but not trivial Truffle plugin that can have production value.</p>
<h3 id="trufflepluginstore">truffle-plugin-store</h3>
<p>The contract we will create is called truffle-plugin-store. It reads the the artifact file of a passed contract name, and sends this artifact to a storage server. We then query the storage server to get the number of stored artifacts for the specific contract name.</p>
<p>For the purpose of this tutorial, I have created a repository with boilerplate code that includes a simple storage server, a basic Truffle project, and the boilerplate code for a Truffle plugin. This repository can be found <a href="https://github.com/rkalis/truffle-plugin-workshop">on GitHub</a>. The repository's <code>master</code> branch contains only this boilerplate code, while the <code>reference</code> branch contains the fully implemented plugin.</p>
<h4 id="prerequisites">Prerequisites</h4>
<p>Before starting, you need to have Node.js, Truffle, and MongoDB installed locally.</p>
<ol>
<li>Install <a href="https://nodejs.org/en/download/">Node.js</a>.</li>
<li>Install Truffle<pre><code class="language-bash">npm install -g truffle
</code></pre>
</li>
<li>Install <a href="https://docs.mongodb.com/manual/installation/">MongoDB</a></li>
</ol>
<h4 id="settinguptherepository">Setting up the repository</h4>
<p>After cloning the repository, there are three components that need to be set up. The first is the artifact storage server, which needs to be running in a separate terminal window during the rest of the development process.</p>
<pre><code class="language-bash">cd artifact-storage-server
npm install
npm start
</code></pre>
<p>The second component is truffle-plugin-store which contains the boilerplate code for a Truffle plugin. The third component is the simplestorage Truffle project that contains a very simple smart contract, and already has truffle-plugin-store included as a dependency. Its dependencies need to be installed, and the contracts need to be compiled.</p>
<pre><code class="language-bash">cd simplestorage
npm install
truffle compile
truffle run store SimpleStorage
</code></pre>
<p>In case of the boilerplate code, this simply outputs 'hello' to the console.</p>
<h4 id="implementingtrufflepluginstore">Implementing truffle-plugin-store</h4>
<p>The boilerplate code contains just the following code inside the <code>store.js</code> file.</p>
<pre><code class="language-js">module.exports = async (config) =&gt; {
  console.log('hello')
}
</code></pre>
<p>The first steps to actually implementing this plugin is figuring out the data we need to receive from the config object. In this case we want to retrieve the contract name, which is a positional argument. Then we want to retrieve this contract's artifact by reading the correct JSON file.</p>
<pre><code class="language-js">module.exports = async (config) =&gt; {
  const contractName = config._[1]
  const contractsBuildDir = config.contracts_build_directory
  const artifactPath = `${contractsBuildDir}/${contractName}.json`
  const artifact = require(artifactPath)
}
</code></pre>
<p>From here on we need to communicate with the artifact storage server. This server has two different endpoints: <code>POST /artifacts</code> which uploads a JSON artifact to the server and <code>GET /artifacts/:contractName</code> which retrieves the artifact count for a specified contract name. To communicate with the server we use the 'axios' library.</p>
<pre><code class="language-bash">npm install axios
</code></pre>
<pre><code class="language-js">const axios = require('axios')

module.exports = async (config) =&gt; {
  const contractName = config._[1]
  const contractsBuildDir = config.contracts_build_directory
  const artifactPath = `${contractsBuildDir}/${contractName}.json`
  const artifact = require(artifactPath)
  
  const url = 'http://localhost:3000/artifacts'
  
  const { data } = await axios.post(url, { artifact })
  console.log(`Stored ${contractName} artifact with id ${data.insertedId}`)
  
  const { data: count } = await axios.get(`${url}/${contractName}`)
  console.log(`${count} ${contractName} artifacts stored in total`)
}
</code></pre>
<p>With these axios calls implemented the plugin now stores the compiled artifacts every time <code>truffle run store SimpleStorage</code> is run, as well as outputting the number of stored artifacts. This concludes the plugins functionality, but there are improvements that can be made to this code, such as an optional URL parameter <code>--url</code> that allows people to use any storage server. A proper plugin also contains extensive error handling, so that users understand when they misuse the plugins.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Truffle plugins can be used to add new functionality or commands to the Truffle CLI. The Truffle CLI provides the plugin with a <em>config</em> object with different parameters that can be used by these Truffle plugins. The truffle-plugin-store example shows the steps required to create a minimal but non-trivial plugin. With this you should be all set to start creating more complex plugins for your own use cases.</p>
<hr>
<p>If you used this guide to create an awesome Truffle plugin of your own, add a PR to the <a href="https://github.com/rkalis/awesome-truffle-plugins">awesome-truffle-plugins</a> and let me know about it in the comments below. If you know 10x developers that want to get started with Truffle plugins, don't forget to share this with them on Facebook, Twitter, and LinkedIn.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Automatically verify Truffle smart contracts on Etherscan]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Etherscan is the most popular explorer in the Ethereum space. And one of its big features is <a href="https://medium.com/etherscan-blog/verifying-contracts-on-etherscan-f995ab772327">verifying the source code of smart contracts</a>. Verification allows users of smart contracts to understand what a contract is doing before using it. This <strong>increases trust in these smart contracts</strong>, and benefits developers</p>]]></description><link>https://kalis.me/verify-truffle-smart-contracts-etherscan/</link><guid isPermaLink="false">5cecc0e6f083a702e65e3d5c</guid><category><![CDATA[ethereum]]></category><category><![CDATA[crypto]]></category><category><![CDATA[truffle]]></category><dc:creator><![CDATA[Rosco Kalis]]></dc:creator><pubDate>Fri, 31 May 2019 14:53:47 GMT</pubDate><media:content url="https://kalis.me/content/images/2019/05/verify-etherscan-2.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://kalis.me/content/images/2019/05/verify-etherscan-2.jpg" alt="Automatically verify Truffle smart contracts on Etherscan"><p>Etherscan is the most popular explorer in the Ethereum space. And one of its big features is <a href="https://medium.com/etherscan-blog/verifying-contracts-on-etherscan-f995ab772327">verifying the source code of smart contracts</a>. Verification allows users of smart contracts to understand what a contract is doing before using it. This <strong>increases trust in these smart contracts</strong>, and benefits developers because <strong>users feel more comfortable</strong> using their smart contracts.</p>
<p>The main way smart contract developers can add verified source code on Etherscan is through the form on their website, but unfortunately <strong>this is a lot of manual work</strong>. You need to enter things like compiler version and constructor parameters, and you need to provide the contract source code in a flattened format that exactly matches the deployed code.</p>
<p>Some developers flatten their Truffle contracts using a command line tool and use the browser-based Remix IDE to deploy the flattened source code. Then they copy the same flattened code to the Etherscan verification form. This is a <strong>cumbersome process that should be automated</strong>.</p>
<p>That is why I created <a href="https://www.npmjs.com/package/truffle-plugin-verify">truffle-plugin-verify</a>, a Truffle plugin that can be used to automatically verify your Truffle contracts through the Etherscan API. With this plugin you can verify your contracts with just a simple command:</p>
<pre><code class="language-shell">truffle run verify ContractName
</code></pre>
<h2 id="prerequisites">Prerequisites</h2>
<p>For this guide we assume you already have a Truffle project with a deployment process set up. If you don't, you can refer to <a href="https://truffleframework.com/tutorials/using-infura-custom-provider">this Truffle tutorial</a> that shows how to set up deployment of Truffle projects with Infura.</p>
<p><strong>Note:</strong> You can also check out the <a href="https://github.com/rkalis/truffle-plugin-verify/tree/master/docs/kalis-me-tutorial-code">source code</a> for this guide on GitHub.</p>
<h2 id="thecontract">The contract</h2>
<p>If you've read any of my <a href="https://kalis.me/check-events-solidity-smart-contract-test-truffle/">previous</a> <a href="https://kalis.me/assert-reverts-solidity-smart-contract-test-truffle/">articles</a>, you know that I'm a fan of using a simple Casino contract as an example. With this contract a player can bet ETH on a number from 1 to 10. To make sure the contract does not go bankrupt, the player can only bet a small percentage of the contract's total balance.</p>
<p>The winning number is generated as a modulo operation on the current block number. This is fine for testing, but be aware that it would be easily abused in production.</p>
<p>In this guide, we will specifically split up the contract further so it's spread out over multiple files. This allows us to showcase the full functionality of the plugin.</p>
<h4 id="contractskillablesol">contracts/Killable.sol</h4>
<pre><code class="language-solidity">pragma solidity ^0.5.8;

contract Killable {
    address payable public owner;

    constructor() public {
        owner = msg.sender;
    }

    function kill() external {
        require(msg.sender == owner, &quot;Only the owner can kill this contract&quot;);
        selfdestruct(owner);
    }
}
</code></pre>
<h4 id="contractscasinosol">contracts/Casino.sol</h4>
<pre><code class="language-solidity">pragma solidity ^0.5.8;

import &quot;./Killable.sol&quot;;

contract Casino is Killable {
    event Play(address payable indexed player, uint256 betSize, uint8 betNumber, uint8 winningNumber);
    event Payout(address payable winner, uint256 payout);

    function fund() external payable {}

    function bet(uint8 number) external payable {
        require(msg.value &lt;= getMaxBet(), &quot;Bet amount can not exceed max bet size&quot;);
        require(msg.value &gt; 0, &quot;A bet should be placed&quot;);

        uint8 winningNumber = generateWinningNumber();
        emit Play(msg.sender, msg.value, number, winningNumber);

        if (number == winningNumber) {
            payout(msg.sender, msg.value * 10);
        }
    }

    function getMaxBet() public view returns (uint256) {
        return address(this).balance / 100;
    }

    function generateWinningNumber() internal view returns (uint8) {
        return uint8(block.number % 10 + 1); // Don't do this in production
    }

    function payout(address payable winner, uint256 amount) internal {
        assert(amount &gt; 0);
        assert(amount &lt;= address(this).balance);

        winner.transfer(amount);
        emit Payout(winner, amount);
    }
}

</code></pre>
<h2 id="verifyingthecontract">Verifying the contract</h2>
<p>Now that we have our contract ready, we can show how simple it is to verify this contract with truffle-plugin-verify.</p>
<h3 id="1installenabletrufflepluginverify">1. Install &amp; enable truffle-plugin-verify</h3>
<p>You can install the Truffle plugin using npm or yarn:</p>
<pre><code class="language-bash">npm install -D truffle-plugin-verify
yarn add -D truffle-plugin-verify
</code></pre>
<p>When it is installed, you should add the following to your <code>truffle-config.js</code> file to enable the plugin with Truffle:</p>
<pre><code class="language-javascript">module.exports = {
  /* ... rest of truffle-config */

  plugins: [
    'truffle-plugin-verify'
  ]
}
</code></pre>
<h3 id="2createanetherscanapikeyandaddittotruffle">2. Create an Etherscan API key and add it to Truffle</h3>
<img src="https://kalis.me/content/images/2020/10/etherscan-api-keys.png" width="100%" alt="Automatically verify Truffle smart contracts on Etherscan">
<p>To create an Etherscan API key, you first need to create an account on the <a href="https://etherscan.io/">Etherscan website</a>. After creating an account, you can add a new API key on your <a href="https://etherscan.io/myapikey">profile page</a>, as seen in the image above. After creating a new key, it should be added to <code>truffle-config.js</code> file under <code>api_keys</code>:</p>
<pre><code class="language-javascript">module.exports = {
  /* ... rest of truffle-config */

  api_keys: {
    etherscan: 'MY_API_KEY'
  }
}
</code></pre>
<p>Of course you shouldn't commit this API key to your Git repository, so I suggest using <a href="https://www.npmjs.com/package/dotenv">dotenv</a> to store the API key in a gitignored <code>.env</code> file and read it from there.</p>
<p>After following these steps, your full config file should look similar to this:</p>
<pre><code class="language-javascript">const HDWalletProvider = require('@truffle/hdwallet-provider');
require('dotenv').config();

module.exports = {
  networks: {
    rinkeby: {
      provider: function() {
        return new HDWalletProvider(
            `${process.env.MNEMONIC}`, 
            `https://rinkeby.infura.io/v3/${process.env.INFURA_ID}`
        )
      },
      network_id: 4
    }
  },
  plugins: [
    'truffle-plugin-verify'
  ],
  api_keys: {
    etherscan: process.env.ETHERSCAN_API_KEY
  }
};
</code></pre>
<p>Your specific config file might be different, but as long as you have a public network deployment set up, and your <code>plugins</code> and <code>api_keys</code> are set correctly, you should be good to go.</p>
<h3 id="3deployverifythecontract">3. Deploy &amp; verify the contract</h3>
<p>Now that everything is set up to use truffle-plugin-verify, the only thing left is to actually deploy and verify the smart contract.</p>
<pre><code class="language-bash">truffle migrate --network rinkeby
</code></pre>
<p>This should take some time, and will show information about the deployment, finally displaying something similar to this:</p>
<pre><code class="language-bash">Summary
=======
&gt; Total deployments:   2
&gt; Final cost:          0.0146786 ETH
</code></pre>
<p>With the contract deployed we can use truffle-plugin-verify to run the Etherscan verification of our Casino contract:</p>
<pre><code class="language-bash">truffle run verify Casino --network rinkeby
</code></pre>
<p>This will again take some time, and eventually return:</p>
<pre><code class="language-bash">Pass - Verified: https://rinkeby.etherscan.io/address/0x2CEA970AE626C8114Ca12942e96c7c2E189C16b2#contracts
</code></pre>
<h2 id="conclusion">Conclusion</h2>
<p>The &quot;traditional&quot; method of Etherscan verification can be cumbersome since there are several manual steps to go through every time you deploy a contract. As an alternative, truffle-plugin-verify offers a <strong>simple and automatic replacement</strong> for the manual verification process. It is easy to install, and can be used to verify any smart contract with a single command.</p>
<hr>
<p>If you found this guide useful and wish to use truffle-plugin-verify inside your own projects, check it out on <a href="https://www.npmjs.com/package/truffle-plugin-verify">npm</a> or <a href="https://github.com/rkalis/truffle-plugin-verify">GitHub</a>. If you enjoyed using the plugin or if you have any suggestions, let me know in the comment section below. And don't forget to share this with your network on Facebook, Twitter and LinkedIn.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[🐼 2018 Year in Review: Blockchain, Open Source & Public Speaking]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Some time ago, I stumbled upon <a href="https://ahmadawais.com/">Ahmad Awais' website</a> after finding his recent VSCode course. Since 2015 Ahmad writes yearly review posts on his blog that outline the important achievements of the year. I enjoy these articles a great deal and they inspired me to start a similar practice on</p>]]></description><link>https://kalis.me/2018-year-in-review/</link><guid isPermaLink="false">5bf93b41cf2d2f039b8d6f0e</guid><category><![CDATA[life]]></category><category><![CDATA[year-review]]></category><dc:creator><![CDATA[Rosco Kalis]]></dc:creator><pubDate>Tue, 08 Jan 2019 13:51:01 GMT</pubDate><media:content url="https://kalis.me/content/images/2019/01/2018-year-in-review.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://kalis.me/content/images/2019/01/2018-year-in-review.jpg" alt="🐼 2018 Year in Review: Blockchain, Open Source & Public Speaking"><p>Some time ago, I stumbled upon <a href="https://ahmadawais.com/">Ahmad Awais' website</a> after finding his recent VSCode course. Since 2015 Ahmad writes yearly review posts on his blog that outline the important achievements of the year. I enjoy these articles a great deal and they inspired me to start a similar practice on my own website.</p>
<p>In this article I will outline my experiences of 2018, and how they have changed me. Every year when I look back, I'm in awe of how much I can change in such a short time. This is the first year I'm writing this review down, which makes 2018 feel even more eventful than the previous years.</p>
<h2 id="writing">Writing</h2>
<p>I've maintained a personal website for several years, but I only started this blog in Q4 of 2017. I started off my blog on a separate subdomain, but last October I had enough of it, so I moved everything to a single website on the kalis.me domain. This was a huge improvement, but it did cause a slight drop in SEO and organic traffic.</p>
<p>When I started, the focus of my blog was on my system setup, with <em><a href="https://kalis.me/dotfiles-automating-macos-system-configuration/">Dotfiles: automating macOS system configuration</a></em> as the cornerstone. In 2018 I shifted towards Ethereum and Blockchain, as you can see in my most-read article <em><a href="https://kalis.me/check-events-solidity-smart-contract-test-truffle/">Checking events when testing Solidity smart contracts with Truffle</a></em>. I only wrote three articles in 2018, a steep drop from four articles in Q4 of 2017 alone. But I did add two new pages with information on <a href="https://kalis.me/about/">myself</a> and <a href="https://kalis.me/speaking/">my public speaking experience</a>.</p>
<p>While the writing slowed down, the readership of my website is still growing, and I currently get between 300-400 unique page views a month. A big part of this is organic traffic, which is growing despite the drop I mentioned. In 2019 I want to further improve the organic traffic by researching more SEO techniques.</p>
<p>I'm currently writing three new articles that I plan to finish in the coming year:</p>
<ul>
<li><em>Automatically back up your Linux server to webDAV cloud storage with Borg Backup</em></li>
<li><em>Testing Oraclize callback functions with Truffle</em></li>
<li><em>Become a verified Brave Publisher and receive BAT Payments on your website</em></li>
</ul>
<p>Tell me what articles you're looking forward to in the comments.</p>
<h2 id="blockchain">Blockchain</h2>
<p>2018 has been all about blockchain for me. I started my blockchain journey when I decided to write my BSc thesis about data validation using blockchain. I built an audit trail that leveraged blockchain as a way to detect tampering with application data. I had to research the possibilities and limitations of blockchain, and I had to change the direction of the project several times. After these hiccups I ended up with a <a href="https://github.com/rkalis/blockchain-audit-trail">working prototype</a> and a <a href="https://esc.fnwi.uva.nl/thesis/centraal/files/f1051832702.pdf">comprehensive thesis</a>.</p>
<h4 id="consensysacademy">ConsenSys Academy</h4>
<p>During my thesis I took some courses on edX and Coursera to better understand Ethereum and blockchain in general. Along the way I found the <a href="https://consensys.net/academy/bootcamp/">ConsenSys Academy</a>, which started a new cohort just as I was finishing my thesis. I signed up, and got accepted for the summer program on a scholarship.</p>
<p>In this bootcamp I learned everything I needed to become a smart contract developer. I was able to apply these new skills in the final practical assignment of the bootcamp. The project I came up with was a <a href="https://github.com/rkalis/ethroulette">decentralised autonomous casino</a> that runs on the Ethereum blockchain.</p>
<p>This project features a roulette game using an oracle to generate a winning number. The casino house is funded by an unlimited number of investors who buy the Roscoin token. The value of this token is always derived from the balance of the casino. This means that all token holders share in the profits and losses of the casino contract.</p>
<p>In my Ethereum projects I employed full unit tests, but it proved difficult to test Ethereum-specific functionality such as events and reverting. I decided to fix my own problems by creating the <a href="https://github.com/rkalis/truffle-assertions/">truffle-assertions</a> library, that solves these issues. It turns out I was not the only one with these problems, as the package currently gets around 3000 monthly downloads on NPM.</p>
<h4 id="trufflecon">TruffleCon</h4>
<p>In October I attended TruffleCon to connect with other people in the Ethereum space. I gave a presentation there, titled <em>Using events to unit test smart contracts</em>. It included several ways a smart contract developer can use events in their unit tests, and I explained how the truffle-assertions library helps with this.</p>
<p><a href="https://youtu.be/0yjlU1vx0HM"><img src="https://kalis.me/content/images/2018/10/sol-cropped-compressed-2.jpg" width="80%" style="border-radius: 5px;" alt="🐼 2018 Year in Review: Blockchain, Open Source & Public Speaking"></a></p>
<p>At the conference I participated in a workshop about Drizzle and I upped my smart contract security game through several smart contract hacking challenges. I also attended talks by other professionals in the blockchain and open source space. Most importantly though, I met a lot of amazing people from all around the world and all kinds of different backgrounds.</p>
<p>TruffleCon was the first conference I ever attended, but it will not be the last. It felt inspiring to be among so many people and to be able to learn from them. At the same time it felt great to be able to share my own knowledge with these people in a meaningful way.</p>
<h4 id="buidlamsterdam">BUIDL Amsterdam</h4>
<img src="https://kalis.me/content/images/2018/12/buidl4-talk-cropped.jpg" width="80%" style="border-radius: 5px;" alt="🐼 2018 Year in Review: Blockchain, Open Source & Public Speaking">
<p>At TruffleCon I met David Truong, who happened to live in Amsterdam like me. He organises monthly Ethereum developer meetups called BUIDL Amsterdam. When I was back in Amsterdam I joined his meetups, and I loved the setup of these events. They are fully focused on developers, and include sessions on all different aspects of Ethereum development. I got more involved with the meetups and I gave a lightning presentation at BUIDL 4.</p>
<h4 id="bitcoincash">Bitcoin Cash</h4>
<p>At this point I was confident in my Ethereum abilities, but I had almost no experience in the larger blockchain space. I was happy that an opportunity arose to get more involved with other blockchain projects as well. BTC.com organised the Amsterdam edition of the BCH DEVCON hackathon at the end of October. I decided to take part, and <a href="https://news.bitcoin.com/pandacash-wins-grand-prize-at-bch-devcon/">I ended up winning as well</a>.</p>
<p><a href="https://news.bitcoin.com/pandacash-wins-grand-prize-at-bch-devcon/"><img src="https://kalis.me/content/images/2019/01/winning-bch-devcon.jpg" width="80%" style="border-radius: 5px;" alt="🐼 2018 Year in Review: Blockchain, Open Source & Public Speaking"></a></p>
<p>I came to the hackathon to learn more about the wider blockchain space, and to see what Bitcoin Cash development looks like. As it turns out, the BCH development ecosystem is way behind Ethereum's, but it does have a lot of potential. At the hackathon I met Adrian Barwicki, Bryan Lee-A-Leong and Nikolay Manolov, and together we set out to make our own contribution to this ecosystem of developer tools.</p>
<p>We created PandaCash, a local development blockchain inspired by Truffle Suite's Ganache. It allows BCH developers to quickly test their applications against this local blockchain. This eliminates the need to connect to a public test network during the initial development stage. You can spin up and shut down clean versions of this development blockchain, which makes it a great tool for running automated tests.</p>
<p>Adrian and I continued working on PandaCash after the hackathon to expand its functionality, while trying to get funding for continued development. Unfortunately this didn't turn out to be fruitful in the chaos of the Bitcoin Cash hard fork and the continued crypto downturn.</p>
<img src="https://kalis.me/content/images/2019/01/honestcash.png" width="50%" alt="🐼 2018 Year in Review: Blockchain, Open Source & Public Speaking">
<p>In the aftermath of the hard fork we did see a different opportunity in the fact that Ryan X. Charles left Bitcoin Cash in favour of Bitcoin SV. At the same time his content platform Yours.org started censoring users, and shut down. Adrian immediately started Honest.cash as an open source and censorship-free version of Yours.org and it picked up steam very fast. I joined Adrian later on when I had more free time, and we're currently working on improving the platform.</p>
<h2 id="opensource">Open Source</h2>
<p>I have always been a big fan of open source software, and a large part of my professional projects at Eurocommercial are already available on GitHub. Many personal projects (such as my <a href="https://github.com/rkalis/ionic-soundboard">ionic-soundboard</a> and my <a href="https://github.com/rkalis/minesweeper">minesweeper game</a>) are also open source. Yet part of the open source philosophy to me is also contributing to the open source projects of other people.</p>
<figure class="kg-card kg-image-card kg-width-full">
    <img src="https://kalis.me/content/images/2019/01/ghost-prs.png" class="kg-image" alt="🐼 2018 Year in Review: Blockchain, Open Source & Public Speaking">
</figure>
<p>So about a year ago I started contributing to other repositories on GitHub. Since then I have contributed to several big open source projects like <a href="https://github.com/TryGhost/Ghost/pulls?utf8=%E2%9C%93&amp;q;=is%3Apr+author%3Arkalis+">Ghost</a> and <a href="https://github.com/Hammerspoon/hammerspoon/pulls?utf8=%E2%9C%93&amp;q;=is%3Apr+author%3Arkalis+">Hammerspoon</a>.</p>
<img src="https://kalis.me/content/images/2019/01/ghprofile.jpg" width="90%" style="border-radius: 5px;" alt="🐼 2018 Year in Review: Blockchain, Open Source & Public Speaking">
<p>My personal projects have also continued to gain traction. My dotfiles repository reached more than 75 stars, and my truffle-assertions library got 40 stars and is getting more than 3000 monthly downloads.</p>
<p>Both of these projects have also attracted outside contributors, so I'd like to give a huge shoutout to <a href="https://github.com/zanawar">Zachary Nawar</a>, <a href="https://github.com/ItsNickBarry">Nick Barry</a> and <a href="https://github.com/zulhfreelancer">Zulhilmi Zainudin</a> for contributing to truffle-assertions and to <a href="https://github.com/emazzotta">Emanuele Mazzotta</a> for contributing to my dotfiles. I also want to thank everyone else that uses my open source projects and opens issues or comments on my repositories.</p>
<h2 id="academics">Academics</h2>
<p>For most of my day to day life, I'm a student, so academics plays a big part in my life. This was a big year in that regard, as I graduated from my Bachelor program in Computer Science last July. After graduation, my thesis supervisor approached me about publishing my thesis work at a conference in December. Over the summer I condensed my 40-page thesis into a 6-page paper for publication, while trying to appeal to a broader audience as well.</p>
<p><a href="https://scholar.google.nl/citations?user=58fbXl8AAAAJ"><img src="https://kalis.me/content/images/2019/01/google_scholar.png" alt="🐼 2018 Year in Review: Blockchain, Open Source & Public Speaking"></a></p>
<p>Luckily, the hard work paid off, as the paper was accepted at the RBchain Workshop at CloudCom 2018 in Cyprus, where I presented it last December.</p>
<p>This year I also started my one-year Master's degree in Software Engineering at the University of Amsterdam. As it's only a one-year program, 2019 will be my final year as a student. After studying for five years this is something look forward to. In April I'm starting my graduation thesis and internship at Bitcoin.com, which lasts until August. So starting September 2019 my student days will be over.</p>
<h2 id="2019">2019</h2>
<p>2018 was an exciting year for me. I graduated from my Bachelor's degree, started my Master's degree, learned Ethereum and blockchain development, won a hackathon, spoke at two conferences, published a paper, created a successful NPM package, and loads more.</p>
<p>I enjoyed 2018, and I'm looking forward to an even more exciting 2019. This year I will speak at more conferences and compete in more hackathons, I will finish my Master's degree, keep contributing to the open source community, and travel to more places.</p>
<hr>
<p>Let me know about your own 2018 in the comments below and share your goals for 2019 with the world. Happy new year!</p>
<!--kg-card-end: markdown--><p></p>]]></content:encoded></item><item><title><![CDATA[Asserting reverts when testing Solidity smart contracts with Truffle]]></title><description><![CDATA[Reverting is a big part of Solidity smart contracts for the Ethereum blockchain. To make sure reverting functionality works well it should be thoroughly tested.]]></description><link>https://kalis.me/assert-reverts-solidity-smart-contract-test-truffle/</link><guid isPermaLink="false">5ba7938122694536af9e5a8e</guid><category><![CDATA[ethereum]]></category><category><![CDATA[truffle]]></category><category><![CDATA[testing]]></category><category><![CDATA[crypto]]></category><dc:creator><![CDATA[Rosco Kalis]]></dc:creator><pubDate>Sat, 13 Oct 2018 15:59:47 GMT</pubDate><media:content url="https://kalis.me/content/images/2018/10/asserting-reverts-1.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><img src="https://kalis.me/content/images/2018/10/asserting-reverts-1.jpg" alt="Asserting reverts when testing Solidity smart contracts with Truffle"><p>Require statements are a great tool to make sure that a Solidity function only gets executed when the right preconditions are met and that the results of functions meet the expectations. By adding require statements early in the functions, we can make sure that no gas gets unnecessarily wasted, and with Truffle v5 we can finally retrieve the revert reason as well. Since require statements and reverting are such a big part of Ethereum smart contracts, it is important that this functionality can easily be tested as well.</p>
<p>The <a href="https://npmjs.com/package/truffle-assertions"><code>truffle-assertions</code></a> library includes the ability to assert that a transaction reverts as expected. It includes support for Truffle v5 revert reason strings, so your tests can include very specific assertions on why the transaction should revert. The library also includes a general function to test for different transaction failures, so if you want to test for out of gas exceptions, it has you covered.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>Before we start, we need <a href="http://truffleframework.com/">Truffle</a>, which can be installed using npm. <strong>Note that this guide is written for Truffle v5</strong>, but with a few changes it will work for Truffle v4 as well. To leverage the strength of reason strings, it is best to use Truffle v5 regardless.</p>
<pre><code class="language-bash">npm install -g truffle@beta
</code></pre>
<p>We also need some sort of Ethereum test network, such as <a href="http://truffleframework.com/ganache/">Ganache</a>, which can be installed from their website, or with Homebrew if you are using macOS. Alternatively, it can be installed as a <a href="https://github.com/trufflesuite/ganache-cli">command line tool</a> with npm. To use revert reason strings, only the command line version can be used.</p>
<pre><code class="language-bash">brew cask install ganache
npm install -g ganache-cli
</code></pre>
<p>Next we create a project directory, and initialise a new Truffle project in it.</p>
<pre><code class="language-bash">mkdir truffle-revert-tests
cd truffle-revert-tests
truffle init
npm init -y
</code></pre>
<p>Finally, the truffle.js configuration file should point to the correct test network. The default port is 7545 for Ganache, and 8545 for ganache-cli.</p>
<pre><code class="language-javascript">module.exports = {
  networks: {
    development: {
      host: &quot;127.0.0.1&quot;,
      port: 8545,
      network_id: &quot;*&quot;
    }
  }
};
</code></pre>
<h2 id="thecontract">The contract</h2>
<p>For this guide, we will use the same smart contract as in <a href="https://kalis.me/check-events-solidity-smart-contract-test-truffle/">my previous article</a>. The contract is a simple number betting contract that allows users to bet ether on a number from 1 to 10. Because we don't want this contract to go bankrupt, the player can only bet a small percentage of the contract's total balance. The winning number is generated as a modulo operation on the current block, which is fine for our testing purpose, but note that it is easily abused if it would be published.</p>
<h4 id="contractscasinosol">contracts/Casino.sol</h4>
<pre><code class="language-solidity">pragma solidity ^0.5.8;

contract Casino {
    address payable public owner;

    event Play(address payable indexed player, uint256 betSize, uint8 betNumber, uint8 winningNumber);
    event Payout(address payable winner, uint256 payout);

    constructor() public {
        owner = msg.sender;
    }

    function kill() external {
        require(msg.sender == owner, &quot;Only the owner can kill this contract&quot;);
        selfdestruct(owner);
    }

    function fund() external payable {}

    function bet(uint8 number) external payable {
        require(msg.value &lt;= getMaxBet(), &quot;Bet amount can not exceed max bet size&quot;);
        require(msg.value &gt; 0, &quot;A bet should be placed&quot;);

        uint8 winningNumber = generateWinningNumber();
        emit Play(msg.sender, msg.value, number, winningNumber);

        if (number == winningNumber) {
            payout(msg.sender, msg.value * 10);
        }
    }

    function getMaxBet() public view returns (uint256) {
        return address(this).balance / 100;
    }

    function generateWinningNumber() internal view returns (uint8) {
        return uint8(block.number % 10 + 1); // Don't do this in production
    }

    function payout(address payable winner, uint256 amount) internal {
        assert(amount &gt; 0);
        assert(amount &lt;= address(this).balance);

        winner.transfer(amount);
        emit Payout(winner, amount);
    }
}
</code></pre>
<h2 id="testingthiscontract">Testing this contract</h2>
<p>In this guide we're interested in testing that the reverting functionality of this contract works as expected. When we look at the <code>bet</code> function of the smart contract, we see that it should only be possible to play when betting more than 0 ether, but less than the max bet, which is set to 1% of the total balance of the contract. To test this, we will verify that the contract reverts when we don't send any ether or when we send more than the max bet. We will also verify that the revert reason strings match the expectations.</p>
<h3 id="installthetestingpackages">Install the testing packages</h3>
<p>First we will install the Chai assertions library (or a different assertions library) for generic assertions, and the truffle-assertions library for the smart contract assertions.</p>
<pre><code class="language-bash">npm install --save-dev chai truffle-assertions
</code></pre>
<h3 id="writethetests">Write the tests</h3>
<p>With the dependencies satisfied, they can be imported at the top of the test, and the correct functions can be used inside the tests. The contract will also be imported at this point.</p>
<pre><code class="language-javascript">const Casino = artifacts.require('Casino');
const assert = require(&quot;chai&quot;).assert;
const truffleAssert = require('truffle-assertions');
</code></pre>
<p>After this, we define a new contract scope, and add <code>beforeEach</code> and <code>afterEach</code> hooks to create a new contract for every test and to tear it down again.</p>
<pre><code class="language-javascript">contract('Casino', (accounts) =&gt; {
    let casino;
    const fundingAccount = accounts[0];
    const bettingAccount = accounts[1];
    const fundingSize = 100;

    // build up and tear down a new Casino contract before each test
    beforeEach(async () =&gt; {
        casino = await Casino.new({ from: fundingAccount });
        await casino.fund({ from: fundingAccount, value: fundingSize });
        assert.equal(await web3.eth.getBalance(casino.address), fundingSize);
    });

    afterEach(async () =&gt; {
        await casino.kill({ from: fundingAccount });
    });
}
</code></pre>
<p>With the plumbing of the test file out of the way, we can add tests for the reverting functions. In the first test we will send 2% of the contract's balance in ether, which should trigger the first require statement. In the second test we won't send any ether, which should trigger the second require statement. Note that the revert reason string can only be used from Truffle v5 onwards, and only with ganache-cli v6.1.3 onwards. If you're using older versions of these tools, the revert reason string should be omitted as a parameter to the <code>truffleAssert.reverts()</code> function.</p>
<pre><code class="language-javascript">it(&quot;should not be able to bet more than max bet&quot;, async () =&gt; {
    let betSize = fundingSize / 50;
    let betNumber = 1;

    await truffleAssert.reverts(
        casino.bet(betNumber, { from: bettingAccount, value: betSize }),
        &quot;Bet amount can not exceed max bet size&quot;
    );
});

it(&quot;should not be able to bet without sending ether&quot;, async () =&gt; {
    let betSize = 0;
    let betNumber = 1;

    await truffleAssert.reverts(
        casino.bet(betNumber, { from: bettingAccount, value: betSize }),
        &quot;A bet should be placed&quot;
    );
});
</code></pre>
<p>Putting these components together we end up with the full test file:</p>
<h4 id="testcasinotestjs">test/Casino.test.js</h4>
<pre><code class="language-javascript">const Casino = artifacts.require('Casino');
const assert = require(&quot;chai&quot;).assert;
const truffleAssert = require('truffle-assertions');

contract('Casino', (accounts) =&gt; {
    let casino;
    const fundingAccount = accounts[0];
    const bettingAccount = accounts[1];
    const fundingSize = 100;

    // build up and tear down a new Casino contract before each test
    beforeEach(async () =&gt; {
        casino = await Casino.new({ from: fundingAccount });
        await casino.fund({ from: fundingAccount, value: fundingSize });
        assert.equal(await web3.eth.getBalance(casino.address), fundingSize);
    });

    afterEach(async () =&gt; {
        await casino.kill({ from: fundingAccount });
    });

    it(&quot;should not be able to bet more than max bet&quot;, async () =&gt; {
        let betSize = fundingSize / 50;
        let betNumber = 1;

        await truffleAssert.reverts(
            casino.bet(betNumber, { from: bettingAccount, value: betSize }),
            &quot;Bet amount can not exceed max bet size&quot;
        );
    });

    it(&quot;should not be able to bet without sending ether&quot;, async () =&gt; {
        let betSize = 0;
        let betNumber = 1;

        await truffleAssert.reverts(
            casino.bet(betNumber, { from: bettingAccount, value: betSize }),
            &quot;A bet should be placed&quot;
        );
    });
});
</code></pre>
<h3 id="runningthetests">Running the tests</h3>
<p>After writing the contract and the tests, we can verify that it is actually working by running all truffle tests.</p>
<pre><code class="language-bash">truffle test
</code></pre>
<p>Which should result in an output similar to this:</p>
<pre><code class="language-bash">Using network 'development'.

Compiling ./contracts/Casino.sol...


  Contract: Casino
    ✓ should not be able to bet more than max bet (47ms)
    ✓ should not be able to bet without sending ether (68ms)


  2 passing (768ms)
</code></pre>
<h2 id="conclusion">Conclusion</h2>
<p>Reverting and require statements are an important part of designing secure smart contracts. The truffle-assertions library allows you to easily test this revert functionality by offering the <code>truffleAssert.reverts()</code> and <code>truffleAssert.fails()</code> functions. These functions make it possible to easily test for reverts or other failures (such as out of gas exceptions). From Truffle v5 onwards, it is also possible to assert the revert reason string to make these revert assertions even more specific.</p>
<p>In this guide, we only went through testing the reverting functionality of the smart contract. Combined with the event testing of <a href="https://kalis.me/check-events-solidity-smart-contract-test-truffle/">my previous article</a>, these tests cover a good part of the contract's functionality.</p>
<hr>
<p>If you found this guide useful and wish to use truffle-assertions for your own use case, check it out on <a href="https://www.npmjs.com/package/truffle-assertions">npm</a> or <a href="https://github.com/rkalis/truffle-assertions">github</a>. If you used this library in testing your own Ethereum smart contracts, tell me about it in the comments below. And don't forget to share this with your smart contract developer friends on Facebook, Twitter and LinkedIn.</p>
<!--kg-card-end: markdown--><!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Checking events when testing Solidity smart contracts with Truffle]]></title><description><![CDATA[Checking emitted events is very powerful when testing smart contracts. Learn how to use the fabric-assertions library to make assertions about smart contract events.]]></description><link>https://kalis.me/check-events-solidity-smart-contract-test-truffle/</link><guid isPermaLink="false">5ba7938122694536af9e5a88</guid><category><![CDATA[ethereum]]></category><category><![CDATA[crypto]]></category><category><![CDATA[truffle]]></category><category><![CDATA[testing]]></category><dc:creator><![CDATA[Rosco Kalis]]></dc:creator><pubDate>Thu, 03 May 2018 20:29:44 GMT</pubDate><media:content url="https://kalis.me/content/images/2018/04/truffle-checking-events-2.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><!--kg-card-begin: markdown--><img src="https://kalis.me/content/images/2018/04/truffle-checking-events-2.jpg" alt="Checking events when testing Solidity smart contracts with Truffle"><p>When testing Solidity smart contracts it is very helpful to check the events generated by those contracts. Especially asserting the arguments of the emitted events is a powerful tool. However, using web3 to watch for events inside tests proved to be impractical, and manually going through the returned transaction receipt can be tedious. This is why we will discuss using <a href="https://npmjs.com/package/truffle-assertions"><code>truffle-assertions</code></a> in order to make straightforward assertions about smart contract events and their arguments.</p>
<p>I created the truffle-assertions library to assert that certain event types were emitted during a transaction, and especially to add complex conditions to the event arguments. In this guide we will explore how to use this library in order to check that certain events were emitted by a test contract and that their arguments fulfil the required conditions.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>Before we start, we need <a href="http://truffleframework.com/">Truffle</a>, which can be installed using npm. <strong>Note that this guide has been updated for Truffle v5</strong>, but with a few changes it will work for Truffle v4 as well.</p>
<pre><code class="language-bash">npm install -g truffle
</code></pre>
<p>We also need some sort of Ethereum test network, such as <a href="http://truffleframework.com/ganache/">Ganache</a>, which can be installed from their website, or with Homebrew if you are using macOS. Alternatively, it can be installed as a <a href="https://github.com/trufflesuite/ganache-cli">command line tool</a> with npm.</p>
<pre><code class="language-bash">brew cask install ganache
npm install -g ganache-cli
</code></pre>
<p>Next we create a project directory, and initialise a new Truffle project in it.</p>
<pre><code class="language-bash">mkdir truffle-event-tests
cd truffle-event-tests
truffle init
npm init -y
</code></pre>
<p>Finally, the truffle.js configuration file should point to the correct test network. The default port is 7545 for Ganache, and 8545 for ganache-cli.</p>
<pre><code class="language-javascript">module.exports = {
  networks: {
    development: {
      host: &quot;127.0.0.1&quot;,
      port: 8545,
      network_id: &quot;*&quot;
    }
  }
};
</code></pre>
<h2 id="thecontract">The contract</h2>
<p>For this guide we will use a simple number betting contract. With this contract a user can bet ether on a number from 1 to 10, getting paid back ten times their initial bet if they pick the correct number. We will also add the possibility to fund the contract. Finally, we will make sure that a player can only bet a small percentage of the contract's balance, so players can not bankrupt the contract by winning too often. The winning number is generated as a modulo operation on the current block, which is fine for our testing purpose, but note that it is easily abused if it would be published.</p>
<h4 id="contractscasinosol">contracts/Casino.sol</h4>
<pre><code class="language-solidity">pragma solidity ^0.5.8;

contract Casino {
    address payable public owner;

    event Play(address payable indexed player, uint256 betSize, uint8 betNumber, uint8 winningNumber);
    event Payout(address payable winner, uint256 payout);

    constructor() public {
        owner = msg.sender;
    }

    function kill() external {
        require(msg.sender == owner, &quot;Only the owner can kill this contract&quot;);
        selfdestruct(owner);
    }

    function fund() external payable {}

    function bet(uint8 number) external payable {
        require(msg.value &lt;= getMaxBet(), &quot;Bet amount can not exceed max bet size&quot;);
        require(msg.value &gt; 0, &quot;A bet should be placed&quot;);

        uint8 winningNumber = generateWinningNumber();
        emit Play(msg.sender, msg.value, number, winningNumber);

        if (number == winningNumber) {
            payout(msg.sender, msg.value * 10);
        }
    }

    function getMaxBet() public view returns (uint256) {
        return address(this).balance / 100;
    }

    function generateWinningNumber() internal view returns (uint8) {
        return uint8(block.number % 10 + 1); // Don't do this in production
    }

    function payout(address payable winner, uint256 amount) internal {
        assert(amount &gt; 0);
        assert(amount &lt;= address(this).balance);

        winner.transfer(amount);
        emit Payout(winner, amount);
    }
}
</code></pre>
<h2 id="testingthiscontract">Testing this contract</h2>
<p>To test this contract's functionality, we will verify that Play events are emitted by the contract, and that its arguments match our expectations. We will also verify that a Payout event is only emitted when the player bets on the correct number, and that their payout is the correct amount.</p>
<h3 id="installthetestingpackages">Install the testing packages</h3>
<p>First we will install the Chai assertions library (or a different assertions library) for generic assertions, and the truffle-assertions library for the event specific assertions.</p>
<pre><code class="language-bash">npm install --save-dev chai truffle-assertions
</code></pre>
<h3 id="writethetests">Write the tests</h3>
<p>With the dependencies satisfied, they can be imported at the top of the test, and the correct functions can be used inside the tests. The contract will also be imported at this point.</p>
<pre><code class="language-javascript">const Casino = artifacts.require('Casino');
const assert = require(&quot;chai&quot;).assert;
const truffleAssert = require('truffle-assertions');
</code></pre>
<p>After this, we define a new contract scope, and add <code>beforeEach</code> and <code>afterEach</code> hooks to create a new contract for every test and to tear it down again.</p>
<pre><code class="language-javascript">contract('Casino', (accounts) =&gt; {
    let casino;
    const fundingAccount = accounts[0];
    const bettingAccount = accounts[1];
    const fundingSize = 100;

    // build up and tear down a new Casino contract before each test
    beforeEach(async () =&gt; {
        casino = await Casino.new({ from: fundingAccount });
        await casino.fund({ from: fundingAccount, value: fundingSize });
        assert.equal(await web3.eth.getBalance(casino.address), fundingSize);
    });

    afterEach(async () =&gt; {
        await casino.kill({ from: fundingAccount });
    });
}
</code></pre>
<p>Since we understand how the algorithm determines the winning number, we know beforehand whether a bet will be winning or losing. So we can define tests for each case.</p>
<p>We will start by defining a test for the losing case. In this test we want to intentionally bet on the wrong number, after which we want to assert that a Play event has been emitted with the correct arguments, but that no Payout event has been emitted, because the player lost the bet. We do this by using the <code>assertEventEmitted</code>, and <code>assertEventNotEmitted</code> functions from the truffle-assertions library. Both take a transaction receipt to look in, the expected event type, and an optional filter function with additional conditions to the event's arguments.</p>
<pre><code class="language-javascript">it(&quot;should lose when bet on the wrong number&quot;, async () =&gt; {
    let betSize = 1;
    // we know what the winning number will be since we know the algorithm
    let betNumber = (await web3.eth.getBlock(&quot;latest&quot;)).number % 10 + 1;

    let tx = await casino.bet(betNumber, { from: bettingAccount, value: betSize });

    // player should be the same as the betting account, and the betted number should not equal the winning number
    truffleAssert.eventEmitted(tx, 'Play', (ev) =&gt; {
        return ev.player === bettingAccount &amp;&amp; !ev.betNumber.eq(ev.winningNumber);
    });
    // there should be no payouts
    truffleAssert.eventNotEmitted(tx, 'Payout');
    // check the contract's balance
    assert.equal(await web3.eth.getBalance(casino.address), fundingSize + betSize);
});
</code></pre>
<p>Finally, we add a test for the winning case, where we want to assert that both a Play and a Payout have been emitted with the correct arguments.</p>
<pre><code class="language-javascript">it(&quot;should win when bet on the right number&quot;, async () =&gt; {
    let betSize = 1;
    // we know what the winning number will be since we know the algorithm
    let betNumber = ((await web3.eth.getBlock(&quot;latest&quot;)).number + 1) % 10 + 1;

    let tx = await casino.bet(betNumber, { from: bettingAccount, value: betSize });

    // player should be the same as the betting account, and the betted number should equal the winning number
    truffleAssert.eventEmitted(tx, 'Play', (ev) =&gt; {
        return ev.player === bettingAccount &amp;&amp; ev.betNumber.eq(ev.winningNumber);
    });
    // player should be the same as the betting account, and the payout should be 10 times the bet size
    truffleAssert.eventEmitted(tx, 'Payout', (ev) =&gt; {
        return ev.winner === bettingAccount &amp;&amp; ev.payout.toNumber() === 10 * betSize;
    });
    // check the contract's balance
    assert.equal(await web3.eth.getBalance(casino.address), fundingSize + betSize - betSize * 10);
});
</code></pre>
<p>When we put everything together we end up with the following smart contract test:</p>
<h4 id="testcasinotestjs">test/Casino.test.js</h4>
<pre><code class="language-javascript">const Casino = artifacts.require('Casino');
const assert = require(&quot;chai&quot;).assert;
const truffleAssert = require('truffle-assertions');

contract('Casino', (accounts) =&gt; {
    let casino;
    const fundingAccount = accounts[0];
    const bettingAccount = accounts[1];
    const fundingSize = 100;

    // build up and tear down a new Casino contract before each test
    beforeEach(async () =&gt; {
        casino = await Casino.new({ from: fundingAccount });
        await casino.fund({ from: fundingAccount, value: fundingSize });
        assert.equal(await web3.eth.getBalance(casino.address), fundingSize);
    });

    afterEach(async () =&gt; {
        await casino.kill({ from: fundingAccount });
    });

    it(&quot;should lose when bet on the wrong number&quot;, async () =&gt; {
        let betSize = 1;
        // we know what the winning number will be since we know the algorithm
        let betNumber = (await web3.eth.getBlock(&quot;latest&quot;)).number % 10 + 1;

        let tx = await casino.bet(betNumber, { from: bettingAccount, value: betSize });

        // player should be the same as the betting account, and the betted number should not equal the winning number
        truffleAssert.eventEmitted(tx, 'Play', (ev) =&gt; {
            return ev.player === bettingAccount &amp;&amp; !ev.betNumber.eq(ev.winningNumber);
        });
        // there should be no payouts
        truffleAssert.eventNotEmitted(tx, 'Payout');
        // check the contract's balance
        assert.equal(await web3.eth.getBalance(casino.address), fundingSize + betSize);
    });

    it(&quot;should win when bet on the right number&quot;, async () =&gt; {
        let betSize = 1;
        // we know what the winning number will be since we know the algorithm
        let betNumber = ((await web3.eth.getBlock(&quot;latest&quot;)).number + 1) % 10 + 1;

        let tx = await casino.bet(betNumber, { from: bettingAccount, value: betSize });

        // player should be the same as the betting account, and the betted number should equal the winning number
        truffleAssert.eventEmitted(tx, 'Play', (ev) =&gt; {
            return ev.player === bettingAccount &amp;&amp; ev.betNumber.eq(ev.winningNumber);
        });
        // player should be the same as the betting account, and the payout should be 10 times the bet size
        truffleAssert.eventEmitted(tx, 'Payout', (ev) =&gt; {
            return ev.winner === bettingAccount &amp;&amp; ev.payout.toNumber() === 10 * betSize;
        });
        // check the contract's balance
        assert.equal(await web3.eth.getBalance(casino.address), fundingSize + betSize - betSize * 10);
    });
});
</code></pre>
<h3 id="runningthetests">Running the tests</h3>
<p>After writing the contract and the tests, we can verify that it is actually working by running all truffle tests. Make sure that a Ganache or ganache-cli instance is running in the background.</p>
<pre><code class="language-bash">truffle test
</code></pre>
<p>Which should result in an output similar to this:</p>
<pre><code class="language-bash">Using network 'development'.

Compiling ./contracts/Casino.sol...


  Contract: Casino
    ✓ should lose when bet on the wrong number (372ms)
    ✓ should win when bet on the right number (490ms)


  2 passing (1s)
</code></pre>
<h2 id="conclusion">Conclusion</h2>
<p>Events are already powerful tools within the smart contract development tool chain, and the truffle-assertions library allows you to use these events to test your smart contracts in a very straightforward way. The library offers a way to add any conditions for the event arguments to these assertions using a filter function, making it easy to look for very specific events.</p>
<p>This guide only discussed checking emitted events as a way to test smart contract functionality. Another strong method is asserting revert functionality of the smart contract, as explained in <a href="https://kalis.me/assert-reverts-solidity-smart-contract-test-truffle/">my other article</a>. Combined, the tests from these two articles cover most of the contract's functionality.</p>
<hr>
<p>If you found this guide useful and wish to use truffle-assertions for your own use case, check it out on <a href="https://www.npmjs.com/package/truffle-assertions">npm</a> or <a href="https://github.com/rkalis/truffle-assertions">github</a>. If you used this library in testing your own Ethereum smart contracts or if you just feel like sharing, tell me about your Dapps in the comments below. And don't forget to share this with your smart contract developer friends on Facebook, Twitter and LinkedIn.</p>
<!--kg-card-end: markdown--><!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>