<?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"><channel><title><![CDATA[// Sean's SWE Journey]]></title><description><![CDATA[Love to learn, develop and share new ideas 👨‍💻]]></description><link>https://skim.foundation</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1715498233905/Nn_Ugi9Re.png</url><title>// Sean&apos;s SWE Journey</title><link>https://skim.foundation</link></image><generator>RSS for Node</generator><lastBuildDate>Thu, 08 Oct 2026 22:20:48 GMT</lastBuildDate><atom:link href="https://skim.foundation/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[My ETHDenver 2025 Experience]]></title><description><![CDATA[This year, I decided to attend ETHDenver, one of the most well-known crypto conferences in the world, from February 25th to March 1st. Although I wasn’t there for the entire week, I made it a priority to experience as much as possible within the few ...]]></description><link>https://skim.foundation/my-ethdenver-2025-experience</link><guid isPermaLink="true">https://skim.foundation/my-ethdenver-2025-experience</guid><category><![CDATA[ETHDenver]]></category><dc:creator><![CDATA[Sean Kim]]></dc:creator><pubDate>Mon, 03 Mar 2025 01:17:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1740965625081/f9b820c0-56d8-4d4d-b994-38ba765de6f5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This year, I decided to attend ETHDenver, one of the most well-known crypto conferences in the world, from February 25th to March 1st. Although I wasn’t there for the entire week, I made it a priority to experience as much as possible within the few days I had. I attended insightful talks, explored countless booths, and checked out a couple of side events. In this post, I’ll share my experience and thoughts on ETHDenver 2025.</p>
<h2 id="heading-stage-talks">Stage Talks</h2>
<p>Before flying out from SFO to Denver, I went through the event schedule and hand-picked the talks I wanted to attend. I was pleasantly surprised by the diverse range of topics such as ZK, AI agents, memecoins, and even non-Ethereum subjects like Bitcoin Ordinals. I also appreciated that there were beginner-friendly talks since I had friends with me who were new to crypto.</p>
<p>However, most stage talks were capped at 25 minutes, which felt too short for many speakers to cover their content effectively. Some sessions felt rushed, with speakers having to skip crucial details. One example was a session on building a full-stack decentralized application on Ethereum. The speaker did an excellent job summarizing the process, but for newcomers, it was a lot to absorb in such a short time. Fortunately, all stage talks were recorded and uploaded to X and YouTube, so attendees (and those who missed out) can revisit them. Still, I wish the talks had been slightly longer to allow for a more engaging and well-paced delivery.</p>
<h2 id="heading-booths">Booths</h2>
<p>After the opening ceremony on February 27th, the expo floor officially opened, revealing a massive space filled with company booths. Given the recent market downturn and the Bybit hack, I was surprised to see such a strong presence from so many companies. It was encouraging to see their continued enthusiasm despite the challenging conditions.</p>
<p>The creativity behind many of the booths was impressive. Some companies, such as Arbitrum, focused on aesthetics with bright neon lighting, while others designed interactive experiences to engage visitors. One of the most memorable booths belonged to EigenLayer, which featured an AI-powered robot that sketched portraits while providing information about the company’s technology.</p>
<p>Walking through the booths was a great way to meet people and learn about different projects. Many companies were eager to share insights about their products and upcoming developments. Of course, there was also an abundance of free merchandise, including stickers, tote bags, T-shirts, and sweatshirts.</p>
<h2 id="heading-side-events">Side Events</h2>
<p>Although these events are referred to as side events, they felt like the main attraction. They were some of the most enjoyable and memorable moments of the conference.</p>
<p>The first event I attended was a game night at the Denver Aquarium. We were given 500 starter chips to play poker, roulette, craps, and blackjack, which made for a fun and competitive evening. In addition to the games, we had full access to explore the aquarium, making it an exciting way to start the trip.</p>
<p>The second event was Lmeow Wolf, which took place at Meow Wolf, an immersive art exhibition. The experience felt futuristic and surreal, almost like stepping into a scene from Back to the Future. We spent time exploring the fourth floor’s exhibits, which were packed with interactive elements and creative installations.</p>
<p>Both events included free food and drinks, which made them even better. If you ever attend ETHDenver, I highly recommend looking into the side events. They offer some of the best opportunities to connect with people while enjoying unique experiences.</p>
<h2 id="heading-my-thoughts">My Thoughts</h2>
<p>ETHDenver 2025 was an incredible experience. My friends and I learned a lot from the talks, discovered exciting projects while exploring the booths, and had a great time at the side events. For anyone looking to network within the crypto space, this conference provides endless opportunities to meet people, from industry veterans to developers and investors. Even for those with different goals, it is still worth attending, especially since most of the events are free. Lastly, if you ever need a break from the conference itself, downtown Denver has plenty of great bars and restaurants to explore.</p>
]]></content:encoded></item><item><title><![CDATA[How I Use AI in Software Development]]></title><description><![CDATA[AI has become an unavoidable topic in tech, with models like OpenAI’s ChatGPT, DeepSeek, and Gemini dominating headlines. Since ChatGPT’s release in November 2022, AI has rapidly changed how we work and build software. Naturally, I’ve been using it e...]]></description><link>https://skim.foundation/how-i-use-ai-in-software-development</link><guid isPermaLink="true">https://skim.foundation/how-i-use-ai-in-software-development</guid><category><![CDATA[AI]]></category><dc:creator><![CDATA[Sean Kim]]></dc:creator><pubDate>Wed, 12 Feb 2025 03:14:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1740951850404/bc0f5985-9b7f-4eb7-afec-55942fe7f341.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>AI has become an unavoidable topic in tech, with models like OpenAI’s ChatGPT, DeepSeek, and Gemini dominating headlines. Since ChatGPT’s release in November 2022, AI has rapidly changed how we work and build software. Naturally, I’ve been using it extensively—especially since leaving Meta in 2023 to co-found a startup. After nearly two years of integrating AI into my workflow, I want to share how I’ve been using it in my software development process.</p>
<h2 id="heading-ai-for-generating-tests"><strong>AI for Generating Tests</strong></h2>
<p>One of the most impactful ways AI improves development is by generating tests. If you’ve written a fair amount of tests, you’ve probably felt the frustration of writing them after implementing a feature. Sometimes, you put it off until the end of the project—or worse, skip it entirely. Writing robust tests requires careful thought:</p>
<ul>
<li><p>What are the key scenarios that need to be covered?</p>
</li>
<li><p>What edge cases should be tested?</p>
</li>
<li><p>How should test data be structured?</p>
</li>
<li><p>Are there third-party dependencies that need mocking?</p>
</li>
</ul>
<p>These questions make test writing a mentally laborious process, and it’s easy to see why many developers struggle with it. But this is where AI can make a huge difference.</p>
<p>Let me share a recent experience. While developing a Sudoku game in Go, I wrote a function to generate an initial puzzle. To ensure its correctness, I needed a unit test that verified multiple conditions:</p>
<ul>
<li><p>Were the expected number of zeroes present?</p>
</li>
<li><p>Did the board have exactly one valid solution?</p>
</li>
<li><p>Were empty cells editable by the user?</p>
</li>
</ul>
<p>On top of these considerations, I had to manually craft all 81 (9×9) cells as test data, which felt incredibly tedious. Instead of spending hours writing boilerplate test cases, I used o3-mini via Cursor, which generated a unit test that met all my requirements in seconds. Of course, I still reviewed and refined the AI-generated test, but this approach saved me significant time. Instead of getting bogged down in writing tests, I could move on and work on implementing other features.</p>
<h2 id="heading-ai-for-naming-things"><strong>AI for Naming Things</strong></h2>
<p>Naming variables, functions, and classes is a fundamental but often underestimated part of software development. A well-chosen name should clearly convey the purpose of the entity while remaining concise and easy to use. However, getting this balance right is tricky. Without clear naming conventions, inconsistency creeps into the codebase, making it harder to read and maintain.</p>
<p>I've spent a fair amount of time getting stuck on naming decisions. Instead of overthinking it, I’ve started asking AI for suggestions. Since AI models have been trained on massive amounts of code, they can suggest widely accepted, intuitive names almost instantly.</p>
<p>Sure, AI-generated names aren’t always perfect, but they provide a strong starting point. More importantly, using AI in this way has encouraged me to write smaller, more modular functions—because when functions are focused on a single task, AI has an easier time understanding their behavior and suggesting relevant names.</p>
<h2 id="heading-learning-from-ai-generated-code"><strong>Learning from AI-Generated Code</strong></h2>
<p>Sometimes, when I need to implement a feature but don’t know where to start, I turn to AI for guidance. It not only provides step-by-step explanations but also introduces new concepts and techniques that I might not have considered. More often than not, the AI’s responses include ideas worth exploring further, turning a simple query into an opportunity for deeper learning.</p>
<p>For instance, while working on my Sudoku game, I wanted to implement a timer. I asked o3-mini to generate the code, and while reviewing it, I noticed an unfamiliar syntax. Curious as a novice Go programmer, I asked follow-up questions and quickly learned that it was using Go channels—a concurrency feature I hadn’t encountered before.</p>
<p>Instead of just copying and pasting the AI’s output, I treated it as a learning opportunity. By engaging with the AI’s response, asking further questions, and reading related documentation, I gained a much deeper understanding of how channels work.</p>
<h2 id="heading-disable-autocompletion"><strong>Disable Autocompletion</strong></h2>
<p>This might just be a personal preference, but I always turn off autocompletion in Cursor. While many developers love it for speeding up coding, I find that it disrupts my thought process. When I’m writing code, I sometimes pause briefly to recall what I had planned to write next. But with autocompletion enabled, suggestions constantly appear, pulling my attention away and interrupting my flow.</p>
<p>Autocompletion undoubtedly has its benefits, but I believe it should be used strategically rather than relied on blindly. Overdependence on it can make it harder to write code independently, and in extreme cases, developers may struggle to recall syntax or structure without AI assistance. That’s why I prefer to keep it off—if I need help, I’d rather ask AI explicitly than have it continuously predict my next move.</p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>AI is a powerful technology that will continue to improve over time. That said, AI is a tool—not a replacement for understanding how software works. Relying on it too much can accelerate development but might come at the cost of deeper learning and problem-solving skills.</p>
<p>Ultimately, the key is balance. Use AI where it adds the most value, but ensure that it aligns with your goals as a developer. Whether you want to speed up your workflow or enhance your learning, AI is most effective when used thoughtfully and deliberately.</p>
]]></content:encoded></item><item><title><![CDATA[[TWIL] Week of August 11, 2024]]></title><description><![CDATA[Hello builders! I hope you all had a fantastic week. Mine was a bit more laid-back. I decided to turn off my "build mode" and dive into some reading. I picked up two books: one technical and one non-technical (though still focused on NFTs). In this p...]]></description><link>https://skim.foundation/twil-08112024</link><guid isPermaLink="true">https://skim.foundation/twil-08112024</guid><category><![CDATA[NFT]]></category><category><![CDATA[SOLID principles]]></category><dc:creator><![CDATA[Sean Kim]]></dc:creator><pubDate>Fri, 23 Aug 2024 05:48:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1724192926501/2ba38d69-5f89-4c41-b2fe-7f994fa97f71.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello builders! I hope you all had a fantastic week. Mine was a bit more laid-back. I decided to turn off my "build mode" and dive into some reading. I picked up two books: one technical and one non-technical (though still focused on NFTs). In this post, I'll share some of the insights I gained from those readings.</p>
<h2 id="heading-agile-software-development">Agile Software Development</h2>
<p>I've been continuing my journey through "Agile Software Development: Principles, Patterns, and Practices." This week, I delved deeper into design principles and a few key patterns. For the purpose of this TWIL series, let’s quickly explore the SOLID principles and a crucial truth about software engineering.</p>
<h3 id="heading-solid-principles">SOLID Principles</h3>
<p>The SOLID principles are a foundational set of guidelines in software development, comprising:</p>
<ul>
<li><p><strong>Single-Responsibility Principle</strong>: A class should have only one reason to change.</p>
</li>
<li><p><strong>Open-Closed Principle</strong>: Software entities should be open for extension but closed for modification.</p>
</li>
<li><p><strong>Liskov Substitution Principle</strong>: Subtypes should be substitutable for their base types.</p>
</li>
<li><p><strong>Interface Segregation Principle</strong>: Clients should not be forced to depend on methods they do not use.</p>
</li>
<li><p><strong>Dependency Inversion Principle</strong>:</p>
<ul>
<li><p>High-level modules should not depend on low-level modules. Both should depend on abstractions.</p>
</li>
<li><p>Abstractions should not depend on details. Details should depend on abstractions.</p>
</li>
</ul>
</li>
</ul>
<p>There’s plenty of resources available online to dive deeper into these principles, so you’ll have no trouble finding detailed explanations and examples.</p>
<p>However, my personal advice is not just to understand what these principles are, but also to know when to apply them. In real-world scenarios, you might need to balance these principles against each other or against other considerations. Being aware of the potential trade-offs and the alternatives available can make a big difference in your decision-making process.</p>
<h3 id="heading-dont-blame-the-requirements-blame-your-code">Don't Blame the Requirements, Blame Your Code</h3>
<p>Besides the SOLID principles, another key takeaway was the realization that software requirements rarely stay constant—they often evolve, and that's completely normal. This means that software needs to be designed with flexibility in mind, able to adapt to changing requirements.</p>
<p>While this might sound obvious, I had a handful of guilty moments reflecting on past experiences where I found myself frustrated with the rigidity of code structures I had written. At times, I even caught myself grumbling about the constantly changing requirements. Moving forward, I’m committed to keeping this truth top of mind and striving to write more maintainable, adaptable code.</p>
<h2 id="heading-the-everything-token">The Everything Token</h2>
<p>I finished reading "The Everything Token" by Steve Kaczynski and Scott Duke Kominers. It’s a solid resource for understanding the principles behind successful NFTs, with plenty of real-world examples. If you’re interested in exploring what NFTs can bring to the business table, this book is a good place to start. I'll be sharing a more detailed review in an upcoming post, so stay tuned!</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>That’s it for this week! I haven’t decided whether I’ll stay in “Read-For-Fun” mode or switch back to “build mode,” but either way, stay tuned for the next post of the TWIL series. Happy hacking! ☕️</p>
]]></content:encoded></item><item><title><![CDATA[[TWIL] Week of August 04, 2024]]></title><description><![CDATA[Hello Builders! Hope you had a wonderful week! For this week’s TWIL post, I’d like to share a slight change to the series moving forward.
As I mentioned in my previous post, I’ll continue writing one TWIL post each week, but the content might shift a...]]></description><link>https://skim.foundation/twil-08042024</link><guid isPermaLink="true">https://skim.foundation/twil-08042024</guid><category><![CDATA[packages]]></category><category><![CDATA[TWIL]]></category><category><![CDATA[agile]]></category><dc:creator><![CDATA[Sean Kim]]></dc:creator><pubDate>Mon, 12 Aug 2024 06:02:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1723410046529/77c9c1f6-e0e2-4422-aa2c-0b82b769631c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello Builders! Hope you had a wonderful week! For this week’s TWIL post, I’d like to share a slight change to the series moving forward.</p>
<p>As I mentioned in my previous post, I’ll continue writing one TWIL post each week, but the content might shift a bit. Instead of diving deep into a couple of technical topics, I’ll provide a high-level overview of what I’ve learned. While these posts might be shorter than before, they’ll still offer enough insight to get you started or to spark your interest. For those of you eager for a deeper dive, don’t worry—I’ll be creating separate, detailed posts on specific topics. So, stay tuned for those!</p>
<p>Now, with that out of the way, let’s jump into what I’ve been exploring this week.</p>
<h2 id="heading-npm-packages">NPM Packages</h2>
<p>While scrolling through my X feed, I stumbled upon an announcement from OnchainKit about adding a React component to their NPM package. Intrigued, I decided to check out their GitHub repository and see if I could contribute. As I browsed through their docs, I noticed a couple of issues that seemed easy to fix. Following their contribution guidelines, I forked the repository, cloned it to my local machine, and started exploring. In the process, I discovered several packages I hadn’t encountered before. Let’s quickly go over them.</p>
<h3 id="heading-vite">Vite</h3>
<p>A front-end build tool and development server designed for speed and efficiency. It offers features like dependency pre-bundling and hot module replacement. If you’re interested in learning more, I recommend checking out their <a target="_blank" href="https://vitejs.dev/">official docs</a>.</p>
<h3 id="heading-vitest">Vitest</h3>
<p>A testing tool tailored for Vite, with an interface similar to Jest. If you’re already using or planning to use Vite, <a target="_blank" href="https://vitest.dev/">this</a> is worth a look.</p>
<h3 id="heading-changeset">Changeset</h3>
<p>A versioning tool for multi-package repositories, making it easy to bump project versions and create changelogs. For those working with multi-package repos, their <a target="_blank" href="https://github.com/changesets/changesets">README</a> is a good starting point.</p>
<h3 id="heading-mdx">MDX</h3>
<p>JSX with markdown capabilities, allowing you to use JSX in markdown files with <code>.mdx</code> extension. I believe it’s particularly useful for creating interactive documentation. See the <a target="_blank" href="https://mdxjs.com/">official doc</a> for more.</p>
<h3 id="heading-vocs">Vocs</h3>
<p>A documentation framework powered by React and Vite. Follow their project structure, and you can quickly generate a documentation website complete with rich features, including navbar, sidebar, and search functionality. It even supports markdown and MDX. For more information, check out their <a target="_blank" href="https://vocs.dev/">official doc</a>.</p>
<p>I haven’t had the chance to dive deep into each of these yet, but I hope this overview gives you a good starting point to explore further.</p>
<h2 id="heading-agile-software-development">Agile Software Development</h2>
<p>Lately, I’ve been reading <em>"Agile Software Development: Principles, Patterns, and Practices"</em> by Uncle Bob, and it’s quickly become one of my favorite books. I’m only up to chapter seven, but it’s already packed with insights not just on Agile development, but on software development in general. Here are a couple of key takeaways as appetizers.</p>
<h3 id="heading-first-test-then-implementation">First Test, Then Implementation</h3>
<p>I used to underestimate the benefits of Test-Driven Development (TDD), thinking it was inefficient to write tests before implementation. However, this book opened my eyes to how TDD can guide better software design by forcing me to think from the perspective of a customer (i.e. person who will use my code). I’m eager to try this approach in my next project.</p>
<h3 id="heading-design-for-now-not-later">Design for Now, Not Later</h3>
<p>This advice is straightforward. If you notice a repeating pattern, apply design principles or patterns to improve it. If not, keep it simple. Overcomplicating your design with unnecessary abstractions can make it harder for others (and even yourself) to understand. I’ll be keeping this in mind in my future development work.</p>
<p>Once I finish the book, I’ll definitely write a separate post to share all the insights I’ve gained.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>That’s it for this week! This TWIL post ended up being a bit lengthy, but I hope you found it helpful. Wishing you all a great week ahead and happy hacking! ☕️</p>
]]></content:encoded></item><item><title><![CDATA[[TWIL] Week of July 28, 2024]]></title><description><![CDATA[Hello builders! This past week has been one of the most emotionally charged experiences I've had in a while. After much thought, I've made the difficult decision to leave Ourkive, the startup I co-founded alongside two other incredible people. While ...]]></description><link>https://skim.foundation/twil-07282024</link><guid isPermaLink="true">https://skim.foundation/twil-07282024</guid><category><![CDATA[life plan]]></category><dc:creator><![CDATA[Sean Kim]]></dc:creator><pubDate>Sun, 11 Aug 2024 05:09:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1723335244607/22b5f8ee-4c35-4cf0-9666-405c09aae03c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello builders! This past week has been one of the most emotionally charged experiences I've had in a while. After much thought, I've made the difficult decision to leave Ourkive, the startup I co-founded alongside two other incredible people. While I plan to tell you more about the reasons behind this move in a separate blog post, today, I want to focus on the future and share my evolving plans as I embark on this next chapter.</p>
<h2 id="heading-whats-next">What's Next?</h2>
<p>You might be wondering, "What's next?" Well, that's a question I've been asking myself too. Although my plans aren't entirely concrete yet, I’m excited to give you a glimpse into what I’m considering.</p>
<h3 id="heading-blogging">Blogging</h3>
<p>So far, my blogging efforts have mostly revolved around the TWIL (This Week I Learned) series. However, I haven't been as consistent as I’d like, often pushing articles to the following week. I owe an apology to those of you following the series. I’ve been playing catch-up, but I’m back on track now. With more control over my schedule, I’m committed to delivering one TWIL article per week moving forward.</p>
<p>In addition to TWIL, I'm considering an idea of starting a new series called "Onchain Study." This series would focus on deep dives into technical learnings and discoveries within the web3 space. Given the depth of the content, these posts will likely be more sporadic than the TWIL entries, but they’ll be rich with insights.</p>
<p>You might wonder if there will be overlap between the TWIL and Onchain Study series. Don't worry, I'll do my best to differentiate the content, possibly even tweaking the structure of TWIL to keep things distinct. Stay tuned for more on that front.</p>
<h3 id="heading-open-source-contributions">Open Source Contributions</h3>
<p>Throughout my software engineering journey, I’ve often heard the advice to contribute to open source projects. For a long time, I didn’t feel ready. I preferred to stay within my comfort zone, focusing on building my own applications. That changed when I joined Meta, where I had the chance to contribute to an open source project called Lexical, a text editor framework with a range of extensible plugins. Even though my contribution was <a target="_blank" href="https://github.com/facebook/lexical/pull/1263">a single commit</a>, the experience of merging a PR into a project I initially felt unqualified to touch was unforgettable.</p>
<p>Now, without the demands of a full-time engineering role, I’m eager to step out of my comfort zone and dive into more open source contributions. I’m sure I’ll encounter some “newbie” moments along the way, but I believe these experiences will help me grow into a stronger engineer.</p>
<h3 id="heading-mastering-design-principles">Mastering Design Principles</h3>
<p>During my time at Ourkive, my focus was primarily on shipping new features fast, which left little room for exploring design principles and best practices. Now, I’m excited to dedicate time to learning these concepts in-depth. Understanding design principles is more than just knowing what they are. It’s about grasping the pros and cons so that I can apply them effectively in the right contexts. I plan to start with some classics by Uncle Bob, and I’m eager to see how these new insights will influence my engineering skills.</p>
<h3 id="heading-reading-for-fun-rff">Reading for Fun (RFF)</h3>
<p>I’ve realized that I need to make more time for reading purely for pleasure. To that end, I’m committing to setting aside a block of time each day to dive into a good book. The genres will vary, but I’m hopeful this will become a refreshing and stress-relieving habit, especially as I navigate this transition.</p>
<h2 id="heading-an-important-intermission">An Important Intermission</h2>
<p>As I take this “intermission,” my ultimate goal is to find a new software engineering role that truly excites me. Before diving back into the job hunt and interview prep, I want to take this time to reflect on my past experiences, strengthen my engineering foundation, and immerse myself in projects alongside brilliant minds. This period of growth and self-discovery will be pivotal as I prepare for the next steps in my career.</p>
<p>Thanks for sticking with me on this TWIL series. Happy hacking! ☕️</p>
]]></content:encoded></item><item><title><![CDATA[[TWIL] Week of July 21, 2024]]></title><description><![CDATA[Hi builders! I’ve been working on making the profile page public and adding a new feature to it. While diving into the platform’s codebase, I wanted to explore the principles of good code design more deeply. Today, I’d like to share what I learned ab...]]></description><link>https://skim.foundation/twil-week-of-july-21-2024</link><guid isPermaLink="true">https://skim.foundation/twil-week-of-july-21-2024</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[Open Closed Principle]]></category><dc:creator><![CDATA[Sean Kim]]></dc:creator><pubDate>Sat, 10 Aug 2024 00:47:58 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1723235684174/e14d48f0-5cc6-4415-84c2-841bb7fb3de5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi builders! I’ve been working on making the profile page public and adding a new feature to it. While diving into the platform’s codebase, I wanted to explore the principles of good code design more deeply. Today, I’d like to share what I learned about the Open-Closed Principle (OCP) and some thoughts on its application.</p>
<h2 id="heading-open-closed-principle">Open-Closed Principle</h2>
<p>The Open-Closed Principle (OCP) is one of the five object-oriented programming principles known as SOLID. The essence of OCP is that a class should be open for extension but closed for modification. Instead of just describing what this means, let’s look at some code to illustrate.</p>
<p>Consider this example:</p>
<pre><code class="lang-typescript"><span class="hljs-built_in">enum</span> DrinkType {
  COKE,
  SPRITE,
  WATER
}

<span class="hljs-keyword">class</span> IngredientAnalyzer {
  sugarAmount(drinkType: DrinkType) {
    <span class="hljs-keyword">if</span> (drinkType === DrinkType.COKE) {
      <span class="hljs-keyword">return</span> <span class="hljs-number">64</span>;
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (drinkType === DrinkType.SPRITE) {
      <span class="hljs-keyword">return</span> <span class="hljs-number">51</span>;
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (drinkType === DrinkType.WATER) {
      <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
    }

    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Not a valid drink type"</span>);
  }
}
</code></pre>
<p>Seems straightforward, right? But what happens when we introduce another drink type? We’d have to update the enum and modify the <code>sugarAmount</code> method by adding another <code>if</code> case:</p>
<pre><code class="lang-typescript"><span class="hljs-built_in">enum</span> DrinkType {
  COKE,
  SPRITE,
  WATER,
  FANTA
}

<span class="hljs-keyword">class</span> IngredientAnalyzer {
  sugarAmount(drinkType: DrinkType) {
    <span class="hljs-keyword">if</span> (drinkType === DrinkType.COKE) {
      <span class="hljs-keyword">return</span> <span class="hljs-number">64</span>;
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (drinkType === DrinkType.SPRITE) {
      <span class="hljs-keyword">return</span> <span class="hljs-number">51</span>;
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (drinkType === DrinkType.WATER) {
      <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (drinkType === DrinkType.FANTA) {
      <span class="hljs-keyword">return</span> <span class="hljs-number">66</span>;
    }

    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Not a valid drink type"</span>);
  }
}
</code></pre>
<p>This works, but what if there’s a way to add new drink types without modifying the <code>sugarAmount</code> method? This is where the OCP comes in.</p>
<p>By converting each enum value into its own class and using an abstract class with the <code>sugarAmount</code> method, we can apply OCP. Here’s how:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">abstract</span> <span class="hljs-keyword">class</span> Drink {
  sugarAmount() {
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
  }
}

<span class="hljs-keyword">class</span> Coke <span class="hljs-keyword">extends</span> Drink {
  sugarAmount() {
    <span class="hljs-keyword">return</span> <span class="hljs-number">61</span>;
  }
}

<span class="hljs-keyword">class</span> SPRITE <span class="hljs-keyword">extends</span> Drink {
  sugarAmount() {
    <span class="hljs-keyword">return</span> <span class="hljs-number">61</span>;
  }
}

<span class="hljs-keyword">class</span> FANTA <span class="hljs-keyword">extends</span> Drink {
  sugarAmount() {
    <span class="hljs-keyword">return</span> <span class="hljs-number">66</span>;
  }
}

<span class="hljs-keyword">class</span> IngredientAnalyzer {
  sugarAmount(drink: Drink) {
    <span class="hljs-keyword">return</span> drink.sugarAmount();
  }
}
</code></pre>
<p>Now, if you want to add a new drink type, simply create a new class that inherits from <code>Drink</code>, without touching the <code>IngredientAnalyzer</code>.</p>
<h3 id="heading-but-what-about-functional-programming">But What About Functional Programming?</h3>
<p>I had the same question until I came across <a target="_blank" href="https://alexnault.dev/open-closed-principle-in-functional-typescript">an article by Alex Nault</a>, which explains how OCP can be expressed through composition. Here’s an example using React components:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Text</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> &lt;p&gt;Hi Builders!&lt;/p&gt;;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Page</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> &lt;Text /&gt;;
}
</code></pre>
<p>What if you want to render a <code>Glimmer</code> component while fetching data from an API? One way is to early return <code>Glimmer</code> within <code>Text</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> Props = {
  isFetching: <span class="hljs-built_in">boolean</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Glimmer</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> &lt;div&gt;This is a glimmer component&lt;/div&gt;;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Text</span>(<span class="hljs-params">{ 
  isFetching
}: Props</span>) </span>{
  <span class="hljs-keyword">if</span> (isFetching) {
    <span class="hljs-keyword">return</span> &lt;Glimmer /&gt;  
  }

  <span class="hljs-keyword">return</span> &lt;p&gt;Hi Builders!&lt;/p&gt;;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Page</span>(<span class="hljs-params">{
  isFetching
}: {
  isFetching: <span class="hljs-built_in">boolean</span>
}</span>) </span>{
  <span class="hljs-keyword">return</span> &lt;Text isFetching={isFetching} /&gt;;
}
</code></pre>
<p>However, instead of the code above, we can apply OCP here by using the <code>children</code> prop:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> Props = {
  isFetching: <span class="hljs-built_in">boolean</span>;
  children: React.ReactNode;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Glimmer</span>(<span class="hljs-params">{
  isFetching,
  children 
}: Props</span>) </span>{
  <span class="hljs-keyword">if</span> (isFetching) {
    <span class="hljs-keyword">return</span> &lt;div&gt;This is a glimmer component&lt;/div&gt;;
  }

  <span class="hljs-keyword">return</span> children;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Text</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> &lt;p&gt;Hi Builders!&lt;/p&gt;;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Page</span>(<span class="hljs-params">{
  isFetching
}: {
  isFetching: <span class="hljs-built_in">boolean</span>
}</span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;Glimmer isFetching={isFetching}&gt;
      &lt;Text /&gt;
    &lt;/Glimmer&gt;
  );
}
</code></pre>
<p>This approach introduces the <code>Glimmer</code> component without modifying the <code>Text</code> component. As it turns out, OCP through composition is widely used in React components, such as <a target="_blank" href="https://react.dev/reference/react/Suspense">the <code>Suspense</code> component</a>.</p>
<h3 id="heading-is-it-actually-beneficial">Is It Actually Beneficial?</h3>
<p>This is a valid question. At first glance, you might think that OCP requires more code, and in simple cases, the benefits might not be immediately apparent. However, as your application grows in complexity, the ability to extend functionality without modifying existing, working code becomes increasingly valuable. In such scenarios, OCP is a principle worth considering.</p>
<p>That’s it for this week! I hope this deep dive into OCP helps you in designing better code. Happy hacking ☕️</p>
]]></content:encoded></item><item><title><![CDATA[[TWIL] Week of July 14, 2024]]></title><description><![CDATA[Hello builders! This week, my focus has been on modifying all our smart contracts to integrate a new multi-sig contract. While I didn't encounter any new lessons to write about, I thought I'd share a couple of Solidity facts I've learned in the past....]]></description><link>https://skim.foundation/twil-07142024</link><guid isPermaLink="true">https://skim.foundation/twil-07142024</guid><category><![CDATA[Solidity]]></category><dc:creator><![CDATA[Sean Kim]]></dc:creator><pubDate>Thu, 08 Aug 2024 03:43:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1723001044822/2e749170-753e-4ad1-91bf-e1db41245773.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello builders! This week, my focus has been on modifying all our smart contracts to integrate a new multi-sig contract. While I didn't encounter any new lessons to write about, I thought I'd share a couple of Solidity facts I've learned in the past.</p>
<h2 id="heading-create-vs-create2">Create vs. Create2</h2>
<p>In Solidity, <code>create</code> and <code>create2</code> are opcodes used for deploying smart contracts. Here's a quick breakdown of their differences:</p>
<ul>
<li><p><strong>Create</strong>: Requires the deployer's address and nonce.</p>
</li>
<li><p><strong>Create2</strong>: Requires 0xff, the deployer's address, a salt, and the bytecode of the smart contract.</p>
</li>
</ul>
<p>Introduced in <a target="_blank" href="https://eips.ethereum.org/EIPS/eip-1014">ERC-1014</a>, <code>create2</code> allows for deterministic contract address generation. This means that if you provide the same arguments to <code>create2</code> twice, you'll get the same contract address both times. This deterministic feature isn't possible with <code>create</code> since it depends on the nonce, which is unique to each transaction made by the sender on the blockchain.</p>
<p>Here are some additional insights:</p>
<ul>
<li><p>When you instantiate a smart contract using the <code>new</code> keyword in Solidity, you're using <code>create</code> under the hood.</p>
</li>
<li><p>State channels often use counterfactual smart contracts, which rely on <code>create2</code>.</p>
</li>
<li><p>The <code>0xFF</code> constant in <code>create2</code> is used to differentiate it from the <code>create</code> opcode.</p>
</li>
</ul>
<p>I'll dive deeper into state channels and counterfactual smart contracts in a future article. For now, let's move on to another Solidity tip.</p>
<h2 id="heading-solidity-constant-variable-getter">Solidity Constant Variable Getter</h2>
<p>In Solidity, you can initialize a constant variable like this:</p>
<pre><code class="lang-solidity"><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Example</span> </span>{
    <span class="hljs-keyword">uint96</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> K <span class="hljs-operator">=</span> <span class="hljs-number">10</span>;
}
</code></pre>
<p>But what if you need to create an interface <code>IExample</code> for the <code>Example</code> contract? How do you write the Solidity code in the interface so that other contracts using <code>IExample</code> can access it?</p>
<p>If you guessed it involves a single line of code, you'd be right. You just need to add a function declaration:</p>
<pre><code class="lang-solidity"><span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">IExample</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">K</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint96</span></span>)</span>;
}
</code></pre>
<p>And, of course, you can call this from another contract like so:</p>
<pre><code class="lang-solidity"><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Caller</span> </span>{
    IExample example;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params">IExample _example</span>) </span>{
        example <span class="hljs-operator">=</span> _example;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getK</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint96</span></span>) </span>{
        <span class="hljs-keyword">return</span> example.K();
    }
}
</code></pre>
<p>Public state variables work the same way as constants in this regard.</p>
<p>That's it for this week! I've shared some random facts about Solidity, and I hope they help you in some way. Happy hacking! ☕️</p>
]]></content:encoded></item><item><title><![CDATA[[TWIL] Week of July 7, 2024]]></title><description><![CDATA[This week, I delved into the intricacies of multi-sig contracts to enhance smart contract upgradability and streamline on-chain data updates. Along the way, I picked up some valuable insights that I'd like to share with you.
Multi-Sig Contracts: An O...]]></description><link>https://skim.foundation/twil-week-of-july-7-2024</link><guid isPermaLink="true">https://skim.foundation/twil-week-of-july-7-2024</guid><category><![CDATA[Solidity]]></category><category><![CDATA[multisig]]></category><category><![CDATA[UUPS]]></category><category><![CDATA[Smart Contracts]]></category><dc:creator><![CDATA[Sean Kim]]></dc:creator><pubDate>Tue, 06 Aug 2024 02:28:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1722035971592/83b8bd99-d48f-47d8-a95e-853090737d97.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This week, I delved into the intricacies of multi-sig contracts to enhance smart contract upgradability and streamline on-chain data updates. Along the way, I picked up some valuable insights that I'd like to share with you.</p>
<h2 id="heading-multi-sig-contracts-an-overview">Multi-Sig Contracts: An Overview</h2>
<p>As the name implies, the core feature of a multi-sig contract is to require multiple signatures to authorize a transaction. Here’s a typical workflow:</p>
<ol>
<li><p><strong>Submission:</strong> One of the contract owners submits a transaction onchain.</p>
</li>
<li><p><strong>Approval:</strong> Other owners approve the submitted transaction.</p>
</li>
<li><p><strong>Execution:</strong> Once the transaction receives enough approvals (i.e., meets a set threshold), one of the owners executes it.</p>
</li>
</ol>
<p>When submitting a transaction, the smart contract must know the essential details, such as the contract address and the encoded function data. The encoded function data can be generated with the following code:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getEncodedFunctionData</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> Contract = <span class="hljs-keyword">await</span> ethers.getContractFactory(<span class="hljs-string">"&lt;contract-name&gt;"</span>);
  <span class="hljs-keyword">const</span> contract = <span class="hljs-keyword">await</span> Contract.attach(<span class="hljs-string">"&lt;deployed-contract-address&gt;"</span>);

  <span class="hljs-keyword">return</span> contract.interface.encodeFunctionData(
    <span class="hljs-string">"&lt;function-name&gt;"</span>, 
    [<span class="hljs-comment">/* args in order */</span>]
  );
}
</code></pre>
<p>With the contract address and encoded function data in hand, the transaction is processed using this line of code:</p>
<pre><code class="lang-solidity">(<span class="hljs-keyword">bool</span> success, <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> returnData) <span class="hljs-operator">=</span> transaction.to.<span class="hljs-built_in">call</span>{
  <span class="hljs-built_in">value</span>: transaction.<span class="hljs-built_in">value</span>
}(transaction.data);
</code></pre>
<p>Here’s a breakdown:</p>
<ul>
<li><p><a target="_blank" href="http://transaction.to"><code>transaction.to</code></a> points to the contract address.</p>
</li>
<li><p><code>call</code> is a low-level method used to interact with another smart contract. If you're familiar with Solidity, you've likely seen it used for sending native tokens to an address. Now, how I'm using it here isn't generally recommended due to <a target="_blank" href="https://solidity-by-example.org/call/">several potential issues</a>, but I opted to use <code>call</code> over <code>delegatecall</code> to maintain the context of the called contract.</p>
</li>
<li><p><code>value</code> is the native token amount being sent, often 0 if not required.</p>
</li>
<li><p><a target="_blank" href="http://transaction.data"><code>transaction.data</code></a> is the encoded function data being passed along.</p>
</li>
</ul>
<p>The <code>returnData</code> is useful for two purposes:</p>
<ol>
<li><p>Handling data returned by the called function.</p>
</li>
<li><p>Capturing errors from the callee contract, though some reverts may not bubble up correctly.</p>
</li>
</ol>
<h2 id="heading-universal-upgradeable-proxy-standard"><strong>Universal Upgradeable Proxy Standard</strong></h2>
<p>The Universal Upgradeable Proxy Standard (UUPS) is a method for upgrading smart contracts. It's similar to the Transparent Proxy Pattern but has key differences in where the authorization and upgrading logic are located.</p>
<p>Here’s an example using OpenZeppelin's <code>UUPSUpgradeable</code> contract:</p>
<pre><code class="lang-solidity"><span class="hljs-keyword">import</span> {<span class="hljs-title">OwnableUpgradeable</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">UUPSUpgradeable</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Example</span> <span class="hljs-keyword">is</span> <span class="hljs-title">OwnableUpgradeable</span>, <span class="hljs-title">UUPSUpgradeable</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initialize</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> owner
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">initializer</span> </span>{
        __Ownable_init(owner);
    }

    <span class="hljs-comment">// ...</span>

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_authorizeUpgrade</span>(<span class="hljs-params"><span class="hljs-keyword">address</span></span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title">onlyOwner</span> </span>{}
}
</code></pre>
<p>Let's take a closer look:</p>
<ul>
<li><p>The <code>Example</code> contract inherits <code>UUPSUpgradeable</code> from OpenZeppelin.</p>
</li>
<li><p>Override the <code>_authorizeUpgrade</code> method from <code>UUPSUpgradeable</code> to include custom authorization logic, such as the <code>onlyOwner</code> modifier, ensuring only the owner can upgrade the contract.</p>
</li>
</ul>
<p>I applied this template to all our smart contracts, ensuring that our multi-sig contract is the only way to authorize upgrades. This means upgradeability authorization relies on the multi-sig mechanism, preventing any single entity from compromising our smart contracts.</p>
<p>That's it! This brief overview hopefully provides a starting point for understanding how multi-sig contracts are implemented and how they can serve as an upgradeability medium. I plan to write a more detailed technical blog on multi-sig contracts in the future. For now, happy hacking! ☕️</p>
]]></content:encoded></item><item><title><![CDATA[[TWIL] Week of June 30, 2024]]></title><description><![CDATA[This week, the first after the OCS Buildathon, I dedicated my time to refactoring and researching upgradability access controls for our smart contracts. Here are the key insights and lessons I learned during this process.
Smart Contract Address Confl...]]></description><link>https://skim.foundation/twil-week-of-june-30-2024</link><guid isPermaLink="true">https://skim.foundation/twil-week-of-june-30-2024</guid><category><![CDATA[openzeppelin-defender]]></category><category><![CDATA[Smart Contracts]]></category><category><![CDATA[Blockchain]]></category><category><![CDATA[safe wallet]]></category><dc:creator><![CDATA[Sean Kim]]></dc:creator><pubDate>Fri, 26 Jul 2024 06:37:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1721022845246/90dbeecf-1e3e-4c57-8ba5-ac4c9eb57b9a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This week, the first after the OCS Buildathon, I dedicated my time to refactoring and researching upgradability access controls for our smart contracts. Here are the key insights and lessons I learned during this process.</p>
<h2 id="heading-smart-contract-address-conflict">Smart Contract Address Conflict</h2>
<p>While refactoring code from the OCS Buildathon, I stumbled upon an unexpected issue: an onboarded artist reported seeing music he didn’t own on his profile page. Knowing the specific API route in question, I delved into the API route handler to identify the problem. Initially, my database query seemed straightforward—fetch all music data where the music NFT smart contract address matches the user's owned contracts. However, our platform supports both Polygon PoS and Base, which complicated matters.</p>
<p>Upon searching for the same smart contract address on both Polygonscan and Basescan, I discovered two distinct smart contracts, each deployed on a different chain. This revealed a critical oversight: smart contract addresses can be identical across different blockchains. For those familiar with how smart contract addresses are generated, this might seem obvious, but it caught me off guard while adding Base support.</p>
<p>Smart contract addresses are generated using the deployer’s address and a nonce, explaining why the two conflicting contracts shared the same address—they had the same deployer and nonce but were on different chains. The lesson here is clear: a smart contract address is unique within a single blockchain but not across different EVM-compatible blockchains.</p>
<p>To resolve the issue, I modified the query to include the blockchain type alongside the smart contract address, ensuring Prisma queries only the correct contract. With this newfound knowledge, we can avoid this pitfall in the future, or at least understand it better if it happens again.</p>
<h2 id="heading-openzeppelin-defender-safe-wallet">OpenZeppelin Defender + Safe Wallet</h2>
<p>Since we had some spare time for maintenance work, I decided to migrate from a company EOA to a multi-sig account. The rationale was simple: sharing our sole private key with an expanding team of developers would be highly imprudent. Our expectations for the new multi-sig account were clear:</p>
<ul>
<li><p>Upgrading smart contracts requires multiple owners' approval</p>
</li>
<li><p>Certain smart contract methods can only be invoked if approved by multiple owners</p>
</li>
<li><p>Seamless support for upgrading smart contracts inheriting OpenZeppelin's upgradeable contracts</p>
</li>
</ul>
<p>For the multi-sig component, Safe wallet came to mind. It's a well-known multi-sig wallet with a web app for easy interaction. However, Safe's app lacked UIs for smart contract upgrades. This led me to discover OpenZeppelin Defender, a SaaS product for smart contract development. It offers features like code inspection, auditing, deployment, and most importantly for us, UIs for deploying and upgrading smart contracts via Safe wallet. To evaluate its suitability, I experimented with deploying and upgrading smart contracts on multiple testnets.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1721970540973/5ef42c23-e101-4d17-a9a5-c5299ba248fa.png" alt class="image--center mx-auto" /></p>
<p>During this testing phase, I encountered several issues:</p>
<ol>
<li><p>Safe wallet isn't yet supported on both Polygon Amoy and Base Sepolia, which are critical for us.</p>
</li>
<li><p>Defender's app felt clunky, with long UI loading times and no error messages for failed processes, making debugging difficult.</p>
</li>
<li><p>The Professional tier pricing is quite steep for an early startup. Although the free tier had no limitations on deploying and upgrading, the pricing page indicated a cap of 5 transactions, which is insufficient.</p>
</li>
</ol>
<p>Despite these downsides, which are understandable given OpenZeppelin's fairly recent public launch of Defender V2, we also identified a few problems with Safe wallet for our purposes:</p>
<ul>
<li><p><a target="_blank" href="https://github.com/safe-global/safe-wallet-web/issues/3881">Safe doesn't have Polygon Amoy support on their roadmap yet</a>. Although it's just a testnet, setting it up again with an EOA incurs significant testnet Matic costs for redeployment if the EOA is compromised.</p>
</li>
<li><p>Safe app lacks fine-grained access control. For instance, requiring a specific EOA for transaction approvals isn't possible since it only supports threshold-based approvals.</p>
</li>
</ul>
<p>Given these challenges, I decided against using both Safe wallet and OpenZeppelin Defender. Instead, I opted to implement our own multi-sig smart contract for access controls. I'll discuss this in detail in the next TWIL article. For now, happy coding, everyone ☕️</p>
]]></content:encoded></item><item><title><![CDATA[[TWIL] Week of June 23, 2024]]></title><description><![CDATA[This week marked the final phase of the Onchain Summer Buildathon, where I concentrated on refactoring code and introducing a new feature for artist and visual artist collaborations. Here are some key takeaways from my work:
Alchemy Webhook Integrati...]]></description><link>https://skim.foundation/twil-06232024</link><guid isPermaLink="true">https://skim.foundation/twil-06232024</guid><category><![CDATA[Solidity]]></category><category><![CDATA[alchemy]]></category><dc:creator><![CDATA[Sean Kim]]></dc:creator><pubDate>Fri, 12 Jul 2024 09:02:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1720503962128/4e8d0398-ab04-401b-8082-054fba4fe427.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This week marked the final phase of the Onchain Summer Buildathon, where I concentrated on refactoring code and introducing a new feature for artist and visual artist collaborations. Here are some key takeaways from my work:</p>
<h2 id="heading-alchemy-webhook-integration">Alchemy Webhook Integration</h2>
<p>Previously, artists on our web app followed these steps to mint a music NFT:</p>
<ol>
<li><p>Click the "Release" button.</p>
</li>
<li><p>The button handler mints an NFT for the artist, approves the NFT marketplace smart contract to transfer the NFT, and lists the NFT on the marketplace.</p>
</li>
<li><p>The artist waits for all transactions to be validated.</p>
</li>
<li><p>Once validated, MongoDB is updated with relevant transaction details, such as the transaction hash.</p>
</li>
</ol>
<p>This process was problematic because some artists would leave the page before transaction validation was complete. To address this issue, we needed a way to trigger a database update automatically in the background once the transaction was validated.</p>
<p>Fortunately, our web app uses Alchemy as the primary node provider. I discovered that Alchemy supports webhooks for various on-chain activities, such as address activity and NFT metadata updates. I opted for the NFT activity webhook, which sends transaction data to an API I created whenever an NFT transfer occurs.</p>
<p>I set up a webhook via the Alchemy web app so that once an NFT is minted, the transaction data is sent to the API. The API then extracts the necessary information from the request and updates the database. As a result, the new user flow is as follows:</p>
<ol>
<li><p>Click the "Release" button.</p>
</li>
<li><p>The button handler mints an NFT for the artist, approves the NFT marketplace smart contract to transfer the NFT, and lists the NFT on the marketplace.</p>
</li>
</ol>
<p>That's it! Artists no longer need to wait for transaction validation. An optimistic update is performed on the client side, while the Alchemy webhook handles transaction validation and database updates in the background.</p>
<p>There are other types of Alchemy webhooks available, so be sure to check out their <a target="_blank" href="https://docs.alchemy.com/reference/notify-api-quickstart">official documentation</a> for more details.</p>
<h2 id="heading-solidity-custom-error">Solidity Custom Error</h2>
<p>Starting from version 0.8.4, Solidity supports custom errors, which can be thrown using the <code>revert</code> keyword. Here's an example:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
pragma solidity ^<span class="hljs-number">0.8</span><span class="hljs-number">.20</span>;

contract Test {
  error NotOwner(address notOwner);

  address owner;

  <span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) {
    owner = msg.sender;
  }

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">checkAndGetOwner</span>(<span class="hljs-params">
    address addr
  </span>) 
    <span class="hljs-title">public</span> 
    <span class="hljs-title">view</span> 
    <span class="hljs-title">returns</span> (<span class="hljs-params">address</span>)
  </span>{
    <span class="hljs-keyword">if</span> (owner != addr) {
      revert NotOwner(addr);
    }

    <span class="hljs-keyword">return</span> owner;
  }
}
</code></pre>
<p>The benefits of using a custom error are twofold: it is more gas-efficient and allows for the creation of dynamic error messages.</p>
<p>To test this code, you can use the <code>revertedWithCustomError</code> method from Chai.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { ethers } <span class="hljs-keyword">from</span> <span class="hljs-string">"hardhat"</span>;
<span class="hljs-keyword">import</span> { expect } <span class="hljs-keyword">from</span> <span class="hljs-string">"chai"</span>;

describe(<span class="hljs-string">"Test"</span>, <span class="hljs-function">() =&gt;</span> {
  it(<span class="hljs-string">"should throw error"</span>, <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> [deployer, account] = <span class="hljs-keyword">await</span> ethers.getSigners();

    <span class="hljs-keyword">const</span> test = <span class="hljs-keyword">await</span> ethers.deployContract(<span class="hljs-string">"Test"</span>);

    <span class="hljs-keyword">await</span> expect(test.checkAndGetOwner(account.address))
      .to.be.revertedWithCustomError(test, <span class="hljs-string">"NotOwner"</span>)
      .withArgs(account.address);
  });
});
</code></pre>
<p>That's a wrap for this week! We've successfully completed and submitted our Onchain Summer Buildathon application. In the coming week, our focus will shift to refactoring a substantial amount of code. Stay tuned for more updates and happy hacking! ☕️</p>
]]></content:encoded></item><item><title><![CDATA[[TWIL] Week of June 16, 2024]]></title><description><![CDATA[This week marked the third week of the Onchain Summer Buildathon, where our focus was on refining our code to support interactions with Externally Owned Accounts (EOAs). Here’s a deep dive into what I learned and achieved during this week of building...]]></description><link>https://skim.foundation/twil-06162024</link><guid isPermaLink="true">https://skim.foundation/twil-06162024</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[hardhat]]></category><category><![CDATA[#SmartWallet  ]]></category><dc:creator><![CDATA[Sean Kim]]></dc:creator><pubDate>Sun, 07 Jul 2024 07:48:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1720021320161/2aeb9a7c-1f2d-4b5e-81bf-2156288bfe65.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This week marked the third week of the Onchain Summer Buildathon, where our focus was on refining our code to support interactions with Externally Owned Accounts (EOAs). Here’s a deep dive into what I learned and achieved during this week of building.</p>
<h2 id="heading-eoa-with-smart-wallet-integration">EOA with Smart Wallet Integration</h2>
<p>Our platform already had artists using EOAs to mint music NFTs, so migrating them entirely to Smart Wallets wasn't feasible. Instead, we had to adapt our client-side code to support both Smart Wallet accounts and EOAs. This involved implementing conditional logic to handle each type of account using the <code>useCapabilities</code> hook.</p>
<p>Here's an example of how we managed batch transactions for both account types:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> Component = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> { data: capabilities } = useCapabilities({
    account,
  });

  <span class="hljs-keyword">const</span> isSmartWallet = <span class="hljs-built_in">Object</span>.keys(capabilities).length &gt; <span class="hljs-number">0</span>;

  <span class="hljs-keyword">const</span> methodOne = {
    <span class="hljs-comment">// args...</span>
  }

  <span class="hljs-keyword">const</span> methodTwo = {
    <span class="hljs-comment">// args...</span>
  }

  <span class="hljs-keyword">const</span> { writeContractsAsync } = useWriteContracts();
  <span class="hljs-keyword">const</span> { writeContractAsync } = useWriteContract();

  <span class="hljs-keyword">const</span> { data: methodOneData } = useSimulateContract(methodOne);
  <span class="hljs-keyword">const</span> { data: methodTwoData } = useSimulateContract({ 
    ...methodTwo, 
    skipSimulation: <span class="hljs-literal">true</span> 
  });

  <span class="hljs-keyword">const</span> handler = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">if</span> (isSmartWallet) {
      <span class="hljs-keyword">await</span> writeContractAsync({
        contracts: [methodOne, methodTwo]
      });
      <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">const</span> hash = <span class="hljs-keyword">await</span> writeContractAsync(methodOneData);
    <span class="hljs-keyword">await</span> waitForTransactionReceipt(config, { hash });
    <span class="hljs-keyword">await</span> writeContractAsync(methodTwoData);
  }
}
</code></pre>
<h3 id="heading-key-learnings-and-highlights">Key Learnings and Highlights</h3>
<ol>
<li><p><strong>Detecting Smart Wallet vs. EOA</strong>: We used the <code>useCapabilities</code> hook to check if the user has capabilities associated with a Smart Wallet. If <code>capabilities</code> has at least one key-value pair, it indicates a Smart Wallet; otherwise, it’s an EOA. The <code>isSmartWallet</code> variable serves as this indicator.</p>
</li>
<li><p><strong>Handling Simulation and Execution</strong>: Include <code>skipSimulation: true</code> on the second <code>useSimulateContract</code> argument if the <code>methodTwo</code> transaction depends on <code>methodOne</code> being executed and confirmed. This avoids errors during simulation. Additionally, invoke <code>waitForTransactionReceipt</code> to ensure the first transaction is confirmed before executing the second one.</p>
</li>
</ol>
<h2 id="heading-hardhat-network-variable">Hardhat Network Variable</h2>
<p>The Hardhat npm package provides many useful variables, including <code>network</code>, which tells you which chain you're executing your script on at runtime. This is particularly useful if you have identical smart contracts deployed on multiple blockchains. Here’s a handy way to get smart contract addresses across supported blockchains:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// main.ts</span>

<span class="hljs-keyword">import</span> { network } <span class="hljs-keyword">from</span> <span class="hljs-string">"hardhat"</span>;

<span class="hljs-built_in">enum</span> EthereumSCAddresses {
  NFT = <span class="hljs-string">"&lt;smart-contract-address&gt;"</span>
}

<span class="hljs-built_in">enum</span> BaseSCAddresses {
  NFT = <span class="hljs-string">"&lt;smart-contract-address&gt;"</span>
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getContractAddress</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">switch</span> (network.name) {
    <span class="hljs-keyword">case</span> <span class="hljs-string">"ethereum"</span>:
      <span class="hljs-keyword">return</span> EthereumSCAddresses
    <span class="hljs-keyword">case</span> <span class="hljs-string">"base"</span>:
      <span class="hljs-keyword">return</span> BaseSCAddresses;
    <span class="hljs-keyword">default</span>:
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Invalid network name"</span>);
  }
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> nftAddress = getContractAddress().NFT;
}
</code></pre>
<p>When you run <code>npx hardhat run main.ts --network ethereum</code>, <code>getContractAddress</code> will return <code>EthereumSCAddresses</code>, and <code>.NFT</code> will return the corresponding smart contract address.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>That’s it for this week! As we head into the final week of the Onchain Summer Buildathon, we'll continue to fix issues and build new features. Happy hacking everyone ☕️!</p>
]]></content:encoded></item><item><title><![CDATA[[TWIL] Week of June 09, 2024]]></title><description><![CDATA[This week marked the second week of the Onchain Summer Buildathon, where I focused on integrating Base, an L2 chain from Coinbase, with Smart Wallet. Here’s a detailed account of what I learned while working on the Smart Wallet integration.
Sign in w...]]></description><link>https://skim.foundation/twil-06092024</link><guid isPermaLink="true">https://skim.foundation/twil-06092024</guid><category><![CDATA[#wagmi]]></category><category><![CDATA[Smart Contracts]]></category><category><![CDATA[#SmartWallet  ]]></category><category><![CDATA[wagmi.sh]]></category><dc:creator><![CDATA[Sean Kim]]></dc:creator><pubDate>Wed, 03 Jul 2024 15:40:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1718689990331/8f29b491-ec0f-4ec1-b7b1-b7b417e53b88.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This week marked the second week of the Onchain Summer Buildathon, where I focused on integrating Base, an L2 chain from Coinbase, with Smart Wallet. Here’s a detailed account of what I learned while working on the Smart Wallet integration.</p>
<h2 id="heading-sign-in-with-ethereum-siwe">Sign in with Ethereum (SIWE)</h2>
<p>Sign in with Ethereum (SIWE) is a popular authentication method that allows decentralized applications (dapps) to remember their users. Our website leverages this method via the siwe npm package. Initially, I assumed that integrating Smart Wallet, a new smart contract wallet from Coinbase, would be straightforward. However, I encountered an unexpected technical hurdle.</p>
<p>Smart Wallet requires dapps to be compatible with <a target="_blank" href="https://eips.ethereum.org/EIPS/eip-6492">EIP-6492</a>, which outlines the signature validation for a pre-deployed smart contract account. Unfortunately, the siwe package doesn’t support this validation yet, meaning it can't validate Smart Wallet’s signature out-of-the-box.</p>
<p>Thankfully, the community already had an answer to this problem. Someone responded to a <a target="_blank" href="https://github.com/spruceid/siwe/issues/148">GitHub issue</a> with a small package that supports both EIP-1271 and EIP-6492 signature validations. This package offers a <code>verifyMessage</code> function that enables us to validate signatures from Smart Wallet seamlessly. If your dapp needs to support SIWE for Smart Wallet, you can use <a target="_blank" href="https://github.com/AmbireTech/signature-validator">AmbireTech's signature-validator package</a>.</p>
<h2 id="heading-usdc-permit">USDC Permit</h2>
<p>Beyond SIWE, our dapp also supports <a target="_blank" href="https://eips.ethereum.org/EIPS/eip-2612">EIP-2612</a>, which allows ERC20 tokens to be approved and transferred within a single transaction. Since USDC has already implemented EIP-2612, our dapp could use a permit function within our NFT marketplace smart contract. However, EIP-2612 doesn't support EIP-1271, meaning a Smart Wallet account signature isn't supported on EIP-2612.</p>
<p>Fortunately, Smart Wallet supports batch transactions. To address this, I separated the permit function logic into approve and transfer transactions and batched them using the <code>useWriteContracts</code> hook from the Wagmi package. Here’s an example code snippet:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { useWriteContracts } <span class="hljs-keyword">from</span> <span class="hljs-string">"wagmi/experimental"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Component</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> usdcApprove = {
    address: <span class="hljs-string">"&lt;usdc-contract-address&gt;"</span>,
    abi,
    functionName: <span class="hljs-string">"approve"</span>,
    args: [<span class="hljs-string">"&lt;spender-address&gt;"</span>, <span class="hljs-string">"&lt;amount&gt;"</span>]
  }

  <span class="hljs-keyword">const</span> usdcTransfer = {
    address: <span class="hljs-string">"&lt;usdc-contract-address&gt;"</span>,
    abi,
    functionName: <span class="hljs-string">"transferFrom"</span>,
    args: [<span class="hljs-string">"&lt;from-address&gt;"</span>, <span class="hljs-string">"&lt;to-address&gt;"</span>, <span class="hljs-string">"&lt;amount&gt;"</span>]
  }

  <span class="hljs-keyword">const</span> { writeContracts } = useWriteContracts();

  <span class="hljs-keyword">const</span> handleUsdc = <span class="hljs-function">() =&gt;</span> {
    writeContracts({
      contracts: [usdcApprove, usdcTransfer]
    });
  }
}
</code></pre>
<p>Although externally owned accounts (EOAs) will have to go through two separate transactions, Smart Wallet accounts will enjoy a simpler user experience with just one click of transaction confirmation.</p>
<p>That’s it for this week. With two weeks left in the OCS Buildathon, I'll continue building and sharing my progress. Happy hacking, everyone! ☕️</p>
]]></content:encoded></item><item><title><![CDATA[[TWIL] Week of June 02, 2024]]></title><description><![CDATA[This week was the first full week of the Onchain Summer Buildathon. I signed up to participate, motivating myself to improve the UX on our platform and build great new features. To start, I upgraded a few npm packages to support the Base network and ...]]></description><link>https://skim.foundation/twil-06022024</link><guid isPermaLink="true">https://skim.foundation/twil-06022024</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[#wagmi]]></category><category><![CDATA[ethers]]></category><dc:creator><![CDATA[Sean Kim]]></dc:creator><pubDate>Tue, 18 Jun 2024 05:50:58 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1718003180377/28c3bddc-a03a-4937-84b1-776600a54e9d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This week was the first full week of the Onchain Summer Buildathon. I signed up to participate, motivating myself to improve the UX on our platform and build great new features. To start, I upgraded a few npm packages to support the Base network and the Smart Wallet launched by the Coinbase Wallet team. Here are some of the things I learned along the way.</p>
<h2 id="heading-caveats-when-upgrading-web3-packages">Caveats When Upgrading Web3 Packages</h2>
<h3 id="heading-wagmi-v1-gt-v2">Wagmi V1 -&gt; V2</h3>
<p>I had to upgrade Wagmi from version V1 to V2. While the complete list of changes is documented <a target="_blank" href="https://wagmi.sh/react/guides/migrate-from-v1-to-v2">here</a>, the information on handling TypeScript types was somewhat lacking in detail.</p>
<p><strong>Wagmi Core Method Types</strong></p>
<p>Besides React hooks, Wagmi also provides regular async methods, such as sendTransaction, powered by a package called viem. In V2, you can also import and use TypeScript types for parameters and return types. However, you need to specify the config type you're using for the generics. Here's an example:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { <span class="hljs-keyword">type</span> SendTransactionParameters, sendTransaction } <span class="hljs-keyword">from</span> <span class="hljs-string">"@wagmi/core"</span>;

<span class="hljs-keyword">const</span> config = createConfig({
  <span class="hljs-comment">// ...</span>
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sendWagmiTransaction</span>(<span class="hljs-params">
  args: SendTransactionParameters&lt;<span class="hljs-keyword">typeof</span> config&gt;,
</span>) </span>{
  <span class="hljs-keyword">const</span> hash = <span class="hljs-keyword">await</span> sendTransaction(config, args);
  <span class="hljs-keyword">return</span> hash;
}
</code></pre>
<p><strong>useWaitForTransactionReceipt Type</strong></p>
<p>Parsing transaction receipt logs is a common task for dapps, and I've been using <code>useWaitForTransactionReceipt</code>, the hook to retrieve the transaction receipt. In V2, you can use <code>WaitForTransactionReceiptData</code>, which is the <code>data</code> type returned by <code>useWaitForTransactionReceipt</code>. Here's how to use it:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> chains = [
  mainnet,
  sepolia,
  goerli,
  polygon,
  polygonAmoy,
  base,
  baseSepolia,
] <span class="hljs-keyword">as</span> <span class="hljs-keyword">const</span>;

<span class="hljs-keyword">const</span> chainIds = chains.map(<span class="hljs-function">(<span class="hljs-params">{ id }</span>) =&gt;</span> id);

<span class="hljs-keyword">type</span> ChainIds = <span class="hljs-keyword">typeof</span> chainIds;

<span class="hljs-keyword">const</span> config = createConfig({
  <span class="hljs-comment">// ...</span>
});

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">parseTxReceiptData</span>(<span class="hljs-params">
  data: WaitForTransactionReceiptData&lt;<span class="hljs-keyword">typeof</span> config, ChainIds[<span class="hljs-built_in">number</span>]&gt;
</span>) </span>{
  <span class="hljs-comment">// ...</span>
}

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { data } = useWaitForTransactionReceipt({ hash: <span class="hljs-string">"&lt;tx-hash&gt;"</span> });
  <span class="hljs-keyword">const</span> parsedData = <span class="hljs-keyword">await</span> parseTxReceiptData(data);
}
</code></pre>
<p>Instead of just passing in <code>config</code>, you also need to pass in a union of all the chain IDs that <code>config</code> supports.</p>
<h3 id="heading-ethers-v5-gt-v6">Ethers V5 -&gt; V6</h3>
<p>Ethers is another package I had to upgrade due to a conflict with Next 14. I was setting up a JSON RPC provider for all the chains we support, but it kept giving me an error message: <code>Could not detect network</code>. I discovered that ethers V5 has an issue with Next 14, which left me with two options:</p>
<ul>
<li><p>Keep ethers V5 and implement a temporary workaround</p>
</li>
<li><p>Upgrade to ethers V6 and make the necessary changes to the codebase</p>
</li>
</ul>
<p>Since I was using wagmi, there wasn't much use of ethers in the codebase, so I chose the second option. If you ever need to use ethers with Next 14, make sure your ethers package is V6 or higher.</p>
<p>That's it for this week. For the next few weeks, I'll be deeply focused on modifying and building new features for the Buildathon. After that, I'll share what I've learned. Happy hacking, everyone ☕️</p>
]]></content:encoded></item><item><title><![CDATA[[TWIL] Week of May 26, 2024]]></title><description><![CDATA[This week, I mainly focused on integrating the smart contracts I deployed last week into our website. I didn't learn much new, so I'll share some useful knowledge I gained in the past that I haven't shared yet.
Solidity Immutable Variable
While I was...]]></description><link>https://skim.foundation/twil-05262024</link><guid isPermaLink="true">https://skim.foundation/twil-05262024</guid><category><![CDATA[hardhat]]></category><category><![CDATA[Solidity]]></category><category><![CDATA[TypeScript]]></category><dc:creator><![CDATA[Sean Kim]]></dc:creator><pubDate>Mon, 10 Jun 2024 06:58:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1717373185057/2f958d41-7211-41da-91a1-a240afa18581.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This week, I mainly focused on integrating the smart contracts I deployed last week into our website. I didn't learn much new, so I'll share some useful knowledge I gained in the past that I haven't shared yet.</p>
<h2 id="heading-solidity-immutable-variable">Solidity Immutable Variable</h2>
<p>While I was skimming through Base's tutorials on gas optimization, I noticed the usage of an immutable variable. Since I was curious, I did a bit of research on what it does.</p>
<h3 id="heading-what-is-it">What is it?</h3>
<p>As the name suggests, an immutable variable cannot be changed after the contract is deployed. You can assign a value while declaring it or within the constructor, but it cannot be accessed until the contract is deployed. In other words, it is not accessible within the constructor; it can only be assigned a value. Here's an example of how it's used:</p>
<pre><code class="lang-solidity"><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Example</span> </span>{
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">immutable</span> addressOne;

    <span class="hljs-keyword">address</span> <span class="hljs-keyword">immutable</span> addressTwo <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>);

    <span class="hljs-keyword">address</span> <span class="hljs-keyword">immutable</span> addressThree;

    <span class="hljs-keyword">address</span> <span class="hljs-keyword">immutable</span> addressFour;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _anotherOwner</span>) </span>{
        addressOne <span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>;
        addressThree <span class="hljs-operator">=</span> _anotherOwner;
        <span class="hljs-comment">// The line below will throw an error</span>
        <span class="hljs-comment">// addressFour = addressOne;</span>
    }
}
</code></pre>
<h3 id="heading-vs-constant-variable">Vs. Constant Variable</h3>
<p>Here's a brief table that explains the differences:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Category</td><td>Constant</td><td>Immutable</td></tr>
</thead>
<tbody>
<tr>
<td>Possible Values</td><td>Any values other than external ones (ex. msg.sender, block.timestamp, etc.)</td><td>Any values other than dynamic ones (ex. string, bytes, etc.)</td></tr>
<tr>
<td>Gas Efficiency</td><td>Most efficient</td><td>Less efficient than a constant variable</td></tr>
<tr>
<td>Value Assignment</td><td>Must be assigned at compilation</td><td>Can be assigned at runtime (i.e. within the constructor)</td></tr>
</tbody>
</table>
</div><h2 id="heading-stuck-transaction">Stuck Transaction</h2>
<p>When using Hardhat to interact with the Polygon PoS network, I frequently faced issues with transactions getting stuck due to low gas prices. This occurred because <a target="_blank" href="https://forum.polygon.technology/t/recommended-min-gas-price-setting/7604">Polygon Labs set a minimum gas price of 30 gwei</a> to mitigate spam transactions. To prevent transactions from getting stuck, you can use one of the following methods:</p>
<ol>
<li><p>Set an explicit gas price for all transactions: If you're using Hardhat, you can set it in <code>hardhat.config.ts</code>:</p>
<pre><code class="lang-typescript"> <span class="hljs-keyword">const</span> config: HardhatUserConfig = {
   <span class="hljs-comment">// ...</span>
   matic: {
     <span class="hljs-comment">// ...</span>
     gasPrice: <span class="hljs-built_in">Number</span>(ethers.parseUnits(<span class="hljs-string">"30"</span>, <span class="hljs-string">"gwei"</span>))
   }
 }
</code></pre>
</li>
<li><p>Set an explicit gas price for a specific transaction:</p>
<pre><code class="lang-solidity"> <span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Example</span> </span>{
     <span class="hljs-keyword">address</span> <span class="hljs-keyword">immutable</span> owner;
     <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) </span>{
         owner <span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>;
     }

     <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setOwner</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> newOwner</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
         owner <span class="hljs-operator">=</span> newOwner;
     }
 }
</code></pre>
<pre><code class="lang-typescript"> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
     <span class="hljs-keyword">const</span> example = <span class="hljs-keyword">await</span> ethers.deployContract(<span class="hljs-string">"Example"</span>);
     <span class="hljs-keyword">await</span> example.waitForDeployment();

     <span class="hljs-keyword">const</span> txOptions = {gasPrice: ethers.parseUnits(<span class="hljs-string">"30"</span>, <span class="hljs-string">"gwei"</span>)};
     <span class="hljs-keyword">const</span> tx = <span class="hljs-keyword">await</span> example.setOwner(ethers.ZeroAddress, txOptions);
     <span class="hljs-keyword">await</span> tx.wait();
     <span class="hljs-comment">// ...</span>
 }

 main();
</code></pre>
</li>
</ol>
<p>If you want to resolve a stuck transaction, you need to provide the nonce of the stuck transaction as well.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> example = <span class="hljs-keyword">await</span> ethers.deployContract(<span class="hljs-string">"Example"</span>);
    <span class="hljs-keyword">await</span> example.waitForDeployment();

    <span class="hljs-keyword">const</span> signer = <span class="hljs-keyword">new</span> ethers.Wallet(
        <span class="hljs-string">`&lt;account-private-key&gt;`</span>,
        ethers.provider,
    );
    <span class="hljs-keyword">const</span> nonce = <span class="hljs-keyword">await</span> ethers.provider.getTransactionCount(
        signer.address,
        <span class="hljs-string">"latest"</span>,
    );

    <span class="hljs-keyword">const</span> txOptions = {gasPrice: ethers.parseUnits(<span class="hljs-string">"30"</span>, <span class="hljs-string">"gwei"</span>), nonce};
    <span class="hljs-keyword">const</span> tx = <span class="hljs-keyword">await</span> example.setOwner(ethers.ZeroAddress, txOptions);
    <span class="hljs-keyword">await</span> tx.wait();
    <span class="hljs-comment">// ...</span>
}

main();
</code></pre>
<p>That wraps up this week's TWIL. Happy hacking! ☕️</p>
]]></content:encoded></item><item><title><![CDATA[[TWIL] Week of May 19, 2024]]></title><description><![CDATA[This week, while working on creating smart contracts, I discovered a couple of great tools from a tutorial in Base's official documentation. I'll share what I learned:
Hardhat Gas Reporter Plugin
This plugin is essential if you're developing with Har...]]></description><link>https://skim.foundation/twil-05192024</link><guid isPermaLink="true">https://skim.foundation/twil-05192024</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[Solidity]]></category><category><![CDATA[hardhat]]></category><dc:creator><![CDATA[Sean Kim]]></dc:creator><pubDate>Mon, 03 Jun 2024 00:02:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1717055547035/44fc37ed-bdc2-4279-9c0e-d28c1c68f298.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This week, while working on creating smart contracts, I discovered a couple of great tools from a <a target="_blank" href="https://docs.base.org/tutorials/hardhat-profiling-gas/">tutorial</a> in Base's official documentation. I'll share what I learned:</p>
<h2 id="heading-hardhat-gas-reporter-plugin">Hardhat Gas Reporter Plugin</h2>
<p>This plugin is essential if you're developing with Hardhat, Truffle, or any other smart contract development tool that supports it. When you run tests, it automatically creates a table of stats, showing the average gas used for each method in your smart contracts. The installation is quick and easy:</p>
<ol>
<li><p>Run <code>npm install -D hardhat-gas-reporter</code></p>
</li>
<li><p>Add the following to <code>hardhat.config.ts</code>:</p>
<pre><code class="lang-typescript"> <span class="hljs-keyword">import</span> <span class="hljs-string">"hardhat-gas-reporter"</span>;
 <span class="hljs-comment">// other imports...</span>

 <span class="hljs-keyword">const</span> config: HardhatUserConfig = {
   <span class="hljs-comment">// other configs...</span>
   gasReporter: {
     enabled: <span class="hljs-literal">true</span>,
   },
 };
</code></pre>
</li>
<li><p>Once you write your smart contract and test code, run <code>npx hardhat test</code>, and you'll see a table of stats.</p>
</li>
</ol>
<p>With the resulting report, you can see how much gas is used for each transaction. This helps you estimate the cost of each transaction and decide if you need to optimize the smart contract code for better gas efficiency.</p>
<h2 id="heading-solidity-optimizer">Solidity Optimizer</h2>
<p>This helps optimize your Solidity code for gas efficiency. To use it, add the following to <code>hardhat.config.ts</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> config: HardhatUserConfig = {
  solidity: {
    <span class="hljs-comment">// ...</span>
    settings: {
      optimizer: {
        enabled: <span class="hljs-literal">true</span>,
        runs: &lt;<span class="hljs-built_in">number</span>&gt;,
      },
    },
  },
};
</code></pre>
<p>Here, <code>&lt;number&gt;</code> depends on your preference. If <code>&lt;number&gt;</code> is high, the optimizer focuses on making the Solidity code more gas-efficient for each method execution, but it costs more to deploy the smart contract. If <code>&lt;number&gt;</code> is low, the deployment cost of the smart contract will be lower, but each method will cost more to execute.</p>
]]></content:encoded></item><item><title><![CDATA[[TWIL] Week of May 12, 2024]]></title><description><![CDATA[Similar to last week, this week was mainly about deploying and upgrading smart contracts. Here are a couple of things I learned:
Usage of unchecked
Looking at many Solidity smart contracts deployed on various mainnets, I noticed the use of the unchec...]]></description><link>https://skim.foundation/twil-05122024</link><guid isPermaLink="true">https://skim.foundation/twil-05122024</guid><category><![CDATA[Solidity]]></category><category><![CDATA[hardhat]]></category><category><![CDATA[openzeppelin]]></category><dc:creator><![CDATA[Sean Kim]]></dc:creator><pubDate>Mon, 27 May 2024 07:03:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1716101455501/9ea6ef16-e031-4555-b68a-b7b65391d967.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Similar to last week, this week was mainly about deploying and upgrading smart contracts. Here are a couple of things I learned:</p>
<h2 id="heading-usage-of-unchecked">Usage of <code>unchecked</code></h2>
<p>Looking at many Solidity smart contracts deployed on various mainnets, I noticed the use of the <code>unchecked</code> keyword in several instances, especially within for loops. I looked it up on Google and read <a target="_blank" href="https://ethereum.stackexchange.com/questions/113221/what-is-the-purpose-of-unchecked-in-solidity">this Stack Overflow answer</a>. In simple terms, it is a gas-efficient way of handling arithmetic operations by ignoring any possible underflow or overflow errors. In other words, if you know that underflow and overflow will never occur for an arithmetic operation, wrapping it with the <code>unchecked</code> keyword will use less gas than the default checked operation. Here's a quick example:</p>
<pre><code class="lang-solidity"><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">SmartContract</span> </span>{
    <span class="hljs-keyword">uint</span>[] <span class="hljs-keyword">public</span> intArray;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">contractMethod</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        <span class="hljs-built_in">require</span>(intArray.<span class="hljs-built_in">length</span> <span class="hljs-operator">&lt;</span> <span class="hljs-number">5</span>, <span class="hljs-string">"Length must be less than 5"</span>);
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint</span> i; i <span class="hljs-operator">&lt;</span> intArray.<span class="hljs-built_in">length</span>;) {
            <span class="hljs-comment">// perform some operation...</span>

            <span class="hljs-keyword">unchecked</span> {
                i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>;
            }
        }
    }
}
</code></pre>
<h2 id="heading-openzeppelins-proxy-admin-issue">OpenZeppelin's Proxy Admin Issue</h2>
<p>While upgrading and deploying several smart contracts using Hardhat, I encountered an error: <code>Proxy admin is not the one registered in the network manifest</code>. I searched online for a solution but found none, except for deleting the entire manifest file in the <code>.openzeppelin</code> directory.</p>
<p>Instead, here's how I resolved it:</p>
<ol>
<li><p>I went to Polygonscan, searched for the contract I was trying to upgrade, and retrieved the proxy admin contract's address by interacting with the website.</p>
</li>
<li><p>I then checked the <code>.openzeppelin &gt; polygon.json</code> file and noticed that the address under <code>admin</code> was different from the one I got from Polygonscan.</p>
</li>
<li><p>I replaced the address in the <code>polygon.json</code> file with the one from Polygonscan.</p>
</li>
<li><p>Finally, I reran the code to upgrade the smart contract, and it worked.</p>
</li>
</ol>
<p>So, if you encounter the same issue, find the proxy admin's address from the relevant block explorer and replace the one in the <code>.openzeppelin &gt; &lt;network&gt;.json</code> file with it.</p>
]]></content:encoded></item><item><title><![CDATA[[TWIL] Week of May 5, 2024]]></title><description><![CDATA[This week, I focused on fixing and refactoring smart contracts to lay the foundation for introducing royalties for artists and collectors. Let me briefly share a few things I've learned.
USDC Permit Domain Name
USDC supports ERC-2612, enabling USDC h...]]></description><link>https://skim.foundation/twil-05052024</link><guid isPermaLink="true">https://skim.foundation/twil-05052024</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[Solidity]]></category><category><![CDATA[hardhat]]></category><category><![CDATA[usdc]]></category><dc:creator><![CDATA[Sean Kim]]></dc:creator><pubDate>Sun, 12 May 2024 05:34:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1715491965279/db2183b2-c48f-445f-9a62-dce17404bcfc.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This week, I focused on fixing and refactoring smart contracts to lay the foundation for introducing royalties for artists and collectors. Let me briefly share a few things I've learned.</p>
<h2 id="heading-usdc-permit-domain-name">USDC Permit Domain Name</h2>
<p>USDC supports ERC-2612, enabling USDC holders to approve a payout without requiring a native token as a transaction fee. I decided to utilize this feature for setting up an NFT purchasing process, but I faced an error message stating <code>EIP2612: invalid signature</code> on the mainnet. It functioned correctly on the testnet, so I wasn't certain about the cause of this problem until I examined their smart contracts on both the testnet and mainnet Etherscans:</p>
<ul>
<li><p><a target="_blank" href="https://sepolia.etherscan.io/address/0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238#readProxyContract">Sepolia Etherscan</a></p>
</li>
<li><p><a target="_blank" href="https://etherscan.io/token/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48#readProxyContract">Mainnet Etherscan</a></p>
</li>
</ul>
<p>If you select the <code>name</code> dropdown, you'll observe that Sepolia Etherscan presents <code>"USDC"</code> while the mainnet Etherscan displays <code>"USD Coin"</code>. This distinction is crucial because the <code>permit</code> method utilizes the <code>name</code> field to authenticate the USDC holder's signature on-chain. My issue stemmed from using a constant string <code>"USDC"</code> instead of retrieving it directly from the contract. Therefore, if you encounter a similar problem, ensure to provide the accurate <code>name</code> based on the blockchain type.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> name = <span class="hljs-keyword">await</span> getUsdcName(chainId);
<span class="hljs-keyword">const</span> signature = <span class="hljs-keyword">await</span> signTypedData({
    domain: {
      name,
      version,
      chainId,
      verifyingContract,
    },
    <span class="hljs-comment">// ...</span>
});

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getUsdcName</span>(<span class="hljs-params">chainId: <span class="hljs-built_in">number</span></span>) </span>{
    <span class="hljs-comment">// Fetch the name value from the USDC contract</span>
}
</code></pre>
<h2 id="heading-typechain">Typechain</h2>
<p>Since I started developing smart contracts, I've been using Hardhat as a tool for deploying and managing smart contracts. However, it wasn't until this week that I discovered <code>typechain</code>. This amazing library automatically creates TypeScript types for each smart contract, making it very simple to know which types are required when working with smart contracts. One way to use this library is by creating a helper function for deploying a smart contract:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { ethers } <span class="hljs-keyword">from</span> <span class="hljs-string">"hardhat"</span>;
<span class="hljs-keyword">import</span> { SmartContractA__factory } <span class="hljs-keyword">from</span> <span class="hljs-string">"/path/to/typechain/dir"</span>;

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deploySmartContractA</span>(<span class="hljs-params">
    deployer: HardhatEthersSigner,
    ...params: Paramters&lt;SmartContractA__factory&gt;
</span>) </span>{
    <span class="hljs-keyword">const</span> smartContractA = <span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> SmartContractA__factory(owner)
        .deploy(...params);
    <span class="hljs-keyword">await</span> smartContractA.waitForDeployment();
    <span class="hljs-keyword">return</span> smartContractA;
}

<span class="hljs-keyword">async</span> test() {
    <span class="hljs-keyword">const</span> [owner] = <span class="hljs-keyword">await</span> ethers.getSigners();
    <span class="hljs-keyword">const</span> args = [];
    <span class="hljs-keyword">const</span> smartContractA = <span class="hljs-keyword">await</span> deploySmartContractA(owner, ...args);

    <span class="hljs-comment">// Assume SmartContractA has setOwner(address owner) external method</span>
    <span class="hljs-keyword">await</span> smartContractA.setOwner(owner.address);

    <span class="hljs-comment">// This will be caught by a linter</span>
    <span class="hljs-comment">// since the argument type is already known</span>
    <span class="hljs-keyword">await</span> smartContractA.setOwner(owner);
}
</code></pre>
<p>Now you know precisely which types are needed for each method!</p>
<h2 id="heading-smart-contract-size-limit">Smart Contract Size Limit</h2>
<p>As I added more methods and tested a smart contract, I noticed all my test cases failing because of the same problem - <code>ProviderError: Error: Transaction reverted: trying to deploy a contract whose code is too large</code>. I soon realized that deploying the smart contract used up more than the default block gas limit (30,000,000). This meant I had to split it into multiple contracts.</p>
<p>I chose to create another contract with a specific purpose: a storage contract. It will store all the on-chain states related to a similar context, along with the getters and setters, but nothing more. Other contracts can access these states by calling the getters and setters through composition. Essentially, the storage contract functions like a database. Here's a simplified example:</p>
<pre><code class="lang-solidity"><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Storage</span> <span class="hljs-keyword">is</span> <span class="hljs-title">OwnableUpgradeable</span> </span>{
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">public</span> nft;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initialize</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> nftAddr</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">initializer</span> </span>{
        __Ownable_init();
        nft <span class="hljs-operator">=</span> nftAddr;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setNft</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> nftAddr</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        <span class="hljs-built_in">require</span>(
            nftAddr <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>),
            <span class="hljs-string">"NFT address cannot be the zero address"</span>
        );
        nft <span class="hljs-operator">=</span> nftAddr;
    }
}

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">PaymentController</span> <span class="hljs-keyword">is</span> 
    <span class="hljs-title">OwnableUpgradeable</span>, 
    <span class="hljs-title">ReentrancyGuardUpgradeable</span> 
</span>{
    Storage <span class="hljs-keyword">public</span> controllerStorage;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initialize</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> storageAddr</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">initializer</span> </span>{
        __Ownable_init();
        controllerStorage <span class="hljs-operator">=</span> Storage(storageAddr);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setControllerStorage</span>(<span class="hljs-params">
        Storage newStorage
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        <span class="hljs-built_in">require</span>(
            <span class="hljs-keyword">address</span>(newStorage) <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>),
            <span class="hljs-string">"New storage address cannot be the zero address"</span>
        );
        controllerStorage <span class="hljs-operator">=</span> newStorage;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">payNftOwner</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">nonReentrant</span> </span>{
      <span class="hljs-built_in">require</span>(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span> <span class="hljs-operator">&gt;</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>, <span class="hljs-string">"Not enough balance"</span>);
      <span class="hljs-keyword">address</span> nftAddr <span class="hljs-operator">=</span> controllerStorage.nft();
      <span class="hljs-keyword">uint256</span> nftBalance <span class="hljs-operator">=</span> IERC721(nftAddr).balanceOf(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
      <span class="hljs-keyword">if</span> (nftBalance <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span>) {
          (<span class="hljs-keyword">bool</span> paid, ) <span class="hljs-operator">=</span> <span class="hljs-keyword">payable</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>).<span class="hljs-built_in">call</span>{<span class="hljs-built_in">value</span>: <span class="hljs-number">1</span> <span class="hljs-literal">ether</span>}(<span class="hljs-string">""</span>);
          <span class="hljs-built_in">require</span>(paid, <span class="hljs-string">"Failed to pay"</span>);
      }
    }
}
</code></pre>
<p>As you can see, I connected these two contracts using composition instead of inheritance to make the <code>Storage</code> contract's states reusable.</p>
]]></content:encoded></item><item><title><![CDATA[Typescript Utility Type - ReturnType]]></title><description><![CDATA[Despite having a reputation of making developers' lives more complicated, Typescript does still provide a huge benefit if you use it well. One of the best ways to use it is to utilize utility types. They are built-in types that you can use to perform...]]></description><link>https://skim.foundation/typescript-utility-type-returntype</link><guid isPermaLink="true">https://skim.foundation/typescript-utility-type-returntype</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[TypeScript Tutorial]]></category><category><![CDATA[Programming Tips]]></category><dc:creator><![CDATA[Sean Kim]]></dc:creator><pubDate>Sat, 30 Dec 2023 00:15:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1703806976646/2766a905-faca-401b-be65-1b526b037997.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Despite having a reputation of making developers' lives more complicated, Typescript does still provide a huge benefit if you use it well. One of the best ways to use it is to utilize <a target="_blank" href="https://www.typescriptlang.org/docs/handbook/utility-types.html#returntypetype">utility types</a>. They are built-in types that you can use to perform safe type-checking on your codebase. In this article, we'll explore one of the utility types - <code>ReturnType</code>.</p>
<h2 id="heading-what-is-returntype">What is <code>ReturnType</code>?</h2>
<p><code>ReturnType</code> allows you to easily create a type that represents whatever a function returns. Let's look at an example:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">f</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> { a: <span class="hljs-number">1</span>, b: <span class="hljs-number">2</span>, }
}

<span class="hljs-comment">// F is now { a: 1, b: 2 } type</span>
<span class="hljs-keyword">type</span> F = ReturnType&lt;<span class="hljs-keyword">typeof</span> f&gt;;
</code></pre>
<p>Pretty simple, right? Well, what's the use case of <code>ReturnType</code>? Let's now look at a simple use case for it.</p>
<h2 id="heading-reduces-type-maintenance-cost">Reduces Type Maintenance Cost</h2>
<p>The power of <code>ReturnType</code> comes into play when we're invoking a same function multiple times in various locations and multiple other functions rely on the values returned by that function. Let's look at an example:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getPosts</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> [
        {
            title: <span class="hljs-string">"Title 1"</span>,
            author: <span class="hljs-string">"Author 1"</span>,
            content: <span class="hljs-string">"Content 1"</span>,
        },
        {
            title: <span class="hljs-string">"Title 2"</span>,
            author: <span class="hljs-string">"Author 2"</span>,
            content: <span class="hljs-string">"Content 2"</span>,
        }, 
        {
            title: <span class="hljs-string">"Title 3"</span>,
            author: <span class="hljs-string">"Author 3"</span>,
            content: <span class="hljs-string">"Content 3"</span>,
        }
    ]
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">removeContent</span>(<span class="hljs-params">
    posts: {
        title: <span class="hljs-built_in">string</span>;
        author: <span class="hljs-built_in">string</span>;
        content: <span class="hljs-built_in">string</span>;
    }[]
</span>) </span>{
    <span class="hljs-keyword">return</span> posts.map(<span class="hljs-function">(<span class="hljs-params">{content, ...rest}</span>) =&gt;</span> rest);
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addDisplayOrder</span>(<span class="hljs-params">
    posts: {
        title: <span class="hljs-built_in">string</span>;
        author: <span class="hljs-built_in">string</span>;
        content: <span class="hljs-built_in">string</span>;
    }[]
</span>) </span>{
    <span class="hljs-keyword">return</span> posts.map(<span class="hljs-function">(<span class="hljs-params">post, index</span>) =&gt;</span> ({ ...post, displayOrder: index }));
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">combineTitleAuthor</span>(<span class="hljs-params">
    posts: {
        title: <span class="hljs-built_in">string</span>;
        author: <span class="hljs-built_in">string</span>;
        content: <span class="hljs-built_in">string</span>;
    }[]
</span>) </span>{
    <span class="hljs-keyword">return</span> posts.map(<span class="hljs-function">(<span class="hljs-params">{title, author, content}</span>) =&gt;</span> ({ titleAuthor: <span class="hljs-string">`<span class="hljs-subst">${title}</span> - <span class="hljs-subst">${author}</span>`</span>, content }));
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> posts = getPosts();

    <span class="hljs-keyword">const</span> postsWithoutContent = removeContent(posts);
    <span class="hljs-keyword">const</span> postsWithDisplayOrder = addDisplayOrder(posts);
    <span class="hljs-keyword">const</span> combinedTitleAuthorPosts = combineTitleAuthor(posts);

    <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>What's "wrong" with this code? If you have keen eyes, you would've already spotted, but we actually have a repeating code, which is the type of posts used as an argument for <code>removeContent</code>, <code>addDisplayOrder</code> and <code>combineTitleAuthor</code>. So here's an improved code:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getPosts</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> [
        {
            title: <span class="hljs-string">"Title 1"</span>,
            author: <span class="hljs-string">"Author 1"</span>,
            content: <span class="hljs-string">"Content 1"</span>,
        },
        {
            title: <span class="hljs-string">"Title 2"</span>,
            author: <span class="hljs-string">"Author 2"</span>,
            content: <span class="hljs-string">"Content 2"</span>,
        }, 
        {
            title: <span class="hljs-string">"Title 3"</span>,
            author: <span class="hljs-string">"Author 3"</span>,
            content: <span class="hljs-string">"Content 3"</span>,
        }
    ]
}

<span class="hljs-keyword">type</span> Posts = {
    title: <span class="hljs-built_in">string</span>;
    author: <span class="hljs-built_in">string</span>;
    content: <span class="hljs-built_in">string</span>;
}[];

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">removeContent</span>(<span class="hljs-params">posts: Posts</span>) </span>{
    <span class="hljs-keyword">return</span> posts.map(<span class="hljs-function">(<span class="hljs-params">{content, ...rest}</span>) =&gt;</span> rest);
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addDisplayOrder</span>(<span class="hljs-params">posts: Posts</span>) </span>{
    <span class="hljs-keyword">return</span> posts.map(<span class="hljs-function">(<span class="hljs-params">post, index</span>) =&gt;</span> ({ ...post, displayOrder: index }));
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">combineTitleAuthor</span>(<span class="hljs-params">posts: Posts</span>) </span>{
    <span class="hljs-keyword">return</span> posts.map(<span class="hljs-function">(<span class="hljs-params">{title, author, content}</span>) =&gt;</span> ({ titleAuthor: <span class="hljs-string">`<span class="hljs-subst">${title}</span> - <span class="hljs-subst">${author}</span>`</span>, content }));
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> posts = getPosts();

    <span class="hljs-keyword">const</span> postsWithoutContent = removeContent(posts);
    <span class="hljs-keyword">const</span> postsWithDisplayOrder = addDisplayOrder(posts);
    <span class="hljs-keyword">const</span> combinedTitleAuthorPosts = combineTitleAuthor(posts);

    <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>Much better! But what happens when we add number of views for each post? Let's take a look:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getPosts</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> [
        {
            title: <span class="hljs-string">"Title 1"</span>,
            author: <span class="hljs-string">"Author 1"</span>,
            content: <span class="hljs-string">"Content 1"</span>,
            views: <span class="hljs-number">1</span>,
        },
        {
            title: <span class="hljs-string">"Title 2"</span>,
            author: <span class="hljs-string">"Author 2"</span>,
            content: <span class="hljs-string">"Content 2"</span>,
            views: <span class="hljs-number">2</span>,
        }, 
        {
            title: <span class="hljs-string">"Title 3"</span>,
            author: <span class="hljs-string">"Author 3"</span>,
            content: <span class="hljs-string">"Content 3"</span>,
            views: <span class="hljs-number">3</span>,
        }
    ]
}

<span class="hljs-keyword">type</span> Posts = {
    title: <span class="hljs-built_in">string</span>;
    author: <span class="hljs-built_in">string</span>;
    content: <span class="hljs-built_in">string</span>;
    <span class="hljs-comment">// Need to add views field</span>
    views: <span class="hljs-built_in">number</span>;
}[];

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">removeContent</span>(<span class="hljs-params">posts: Posts</span>) </span>{
    <span class="hljs-keyword">return</span> posts.map(<span class="hljs-function">(<span class="hljs-params">{content, ...rest}</span>) =&gt;</span> rest);
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addDisplayOrder</span>(<span class="hljs-params">posts: Posts</span>) </span>{
    <span class="hljs-keyword">return</span> posts.map(<span class="hljs-function">(<span class="hljs-params">post, index</span>) =&gt;</span> ({ ...post, displayOrder: index }));
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">combineTitleAuthor</span>(<span class="hljs-params">posts: Posts</span>) </span>{
    <span class="hljs-keyword">return</span> posts.map(<span class="hljs-function">(<span class="hljs-params">{title, author, content}</span>) =&gt;</span> ({ titleAuthor: <span class="hljs-string">`<span class="hljs-subst">${title}</span> - <span class="hljs-subst">${author}</span>`</span>, content }));
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> posts = getPosts();

    <span class="hljs-keyword">const</span> postsWithoutContent = removeContent(posts);
    <span class="hljs-keyword">const</span> postsWithDisplayOrder = addDisplayOrder(posts);
    <span class="hljs-keyword">const</span> combinedTitleAuthorPosts = combineTitleAuthor(posts);

    <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>Once we added <code>views</code> field to each post, we had to add <code>views: number;</code> to <code>Posts</code> type. If you're used to construct a type this way, you may think that it's the best we can do. However, you can actually improve this further by utilizing <code>ReturnType</code>.</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getPosts</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> [
        {
            title: <span class="hljs-string">"Title 1"</span>,
            author: <span class="hljs-string">"Author 1"</span>,
            content: <span class="hljs-string">"Content 1"</span>,
            views: <span class="hljs-number">1</span>,
        },
        {
            title: <span class="hljs-string">"Title 2"</span>,
            author: <span class="hljs-string">"Author 2"</span>,
            content: <span class="hljs-string">"Content 2"</span>,
            views: <span class="hljs-number">2</span>,
        }, 
        {
            title: <span class="hljs-string">"Title 3"</span>,
            author: <span class="hljs-string">"Author 3"</span>,
            content: <span class="hljs-string">"Content 3"</span>,
            views: <span class="hljs-number">3</span>,
        }
    ]
}

<span class="hljs-keyword">type</span> Posts = ReturnType&lt;<span class="hljs-keyword">typeof</span> getPosts&gt;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">removeContent</span>(<span class="hljs-params">posts: Posts</span>) </span>{
    <span class="hljs-keyword">return</span> posts.map(<span class="hljs-function">(<span class="hljs-params">{content, ...rest}</span>) =&gt;</span> rest);
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addDisplayOrder</span>(<span class="hljs-params">posts: Posts</span>) </span>{
    <span class="hljs-keyword">return</span> posts.map(<span class="hljs-function">(<span class="hljs-params">post, index</span>) =&gt;</span> ({ ...post, displayOrder: index }));
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">combineTitleAuthor</span>(<span class="hljs-params">posts: Posts</span>) </span>{
    <span class="hljs-keyword">return</span> posts.map(<span class="hljs-function">(<span class="hljs-params">{title, author, content}</span>) =&gt;</span> ({ titleAuthor: <span class="hljs-string">`<span class="hljs-subst">${title}</span> - <span class="hljs-subst">${author}</span>`</span>, content }));
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> posts = getPosts();

    <span class="hljs-keyword">const</span> postsWithoutContent = removeContent(posts);
    <span class="hljs-keyword">const</span> postsWithDisplayOrder = addDisplayOrder(posts);
    <span class="hljs-keyword">const</span> combinedTitleAuthorPosts = combineTitleAuthor(posts);

    <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>We've switched from using <code>{ title: string; author: string; content: string; views: number; }[]</code> to <code>ReturnType&lt;typeof getPosts&gt;</code>. You might wonder why. Before, the <code>Posts</code> and the return type of <code>getPosts</code> function were not aligned, meaning any changes in <code>getPosts</code>' return type had to be manually updated in <code>Posts</code>. Now, with the current code, <code>Posts</code> and <code>getPosts</code> are in sync, ensuring that any changes in the return type of <code>getPosts</code> are automatically reflected in <code>Posts</code>. This might seem trivial, but it's quite beneficial, especially as your codebase grows. A larger codebase means longer type-checking times by your linter, and this synchronization reduces the need for additional iterations, which can be quite impactful.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Utilizing TypeScript's <code>ReturnType</code> simplifies code management by automatically syncing type definitions, reducing manual updates and maintenance overhead. This not only enhances consistency across the codebase but also streamlines development, particularly in large-scale projects. Happy hacking 👨‍💻!</p>
]]></content:encoded></item><item><title><![CDATA[Leveraging TypeScript Type Guards for Precise Filtering in Arrays]]></title><description><![CDATA[Since functional programming gained popularity in the JavaScript world, a trio of array methods—namely, map, filter, and reduce—have also risen to prominence. These methods became go-to tools for two primary reasons:

They're simpler for doing basic ...]]></description><link>https://skim.foundation/leveraging-typescript-type-guards-for-precise-filtering-in-arrays</link><guid isPermaLink="true">https://skim.foundation/leveraging-typescript-type-guards-for-precise-filtering-in-arrays</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Functional Programming]]></category><dc:creator><![CDATA[Sean Kim]]></dc:creator><pubDate>Tue, 14 Nov 2023 08:42:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1715407504123/106e4ae9-fa4a-4690-8f71-bd68856a0ed6.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Since functional programming gained popularity in the JavaScript world, a trio of array methods—namely, <code>map</code>, <code>filter</code>, and <code>reduce</code>—have also risen to prominence. These methods became go-to tools for two primary reasons:</p>
<ol>
<li><p>They're simpler for doing basic looping with arrays.</p>
</li>
<li><p>They allow method chaining, resembling the builder design pattern, enhancing coding flexibility.</p>
</li>
</ol>
<h2 id="heading-problem">Problem</h2>
<p>However, there's a catch when using TypeScript's <code>filter</code>. Consider the following code:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> posts = [
    {id: <span class="hljs-number">0</span>, title: <span class="hljs-string">"Title 1"</span>, content: <span class="hljs-string">"Content 1"</span>},
    {id: <span class="hljs-number">1</span>, title: <span class="hljs-string">"Title 2"</span>, content: <span class="hljs-string">"Content 2"</span>},
    {id: <span class="hljs-number">2</span>, title: <span class="hljs-string">"Title 3"</span>, content: <span class="hljs-string">"Content 3"</span>},
    <span class="hljs-literal">null</span>,
    <span class="hljs-literal">undefined</span>
];

<span class="hljs-keyword">const</span> filteredPosts = posts.filter(<span class="hljs-function"><span class="hljs-params">post</span> =&gt;</span> post != <span class="hljs-literal">null</span>);
</code></pre>
<p>Surprisingly, TypeScript infers <code>filteredPosts</code> as a mix of valid posts and null values (i.e. <code>({id: number, title: string, content: string} | null)[]</code>). How can we ensure TypeScript infers the correct type?</p>
<h2 id="heading-type-guard">Type Guard</h2>
<p>Fortunately, TypeScript offers a solution through type guards, expressions that perform runtime checks, ensuring correct type inference. Let's see it in action:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Constructed this type for convenience</span>
<span class="hljs-keyword">type</span> Post = {id: <span class="hljs-built_in">number</span>, title: <span class="hljs-built_in">string</span>, content: <span class="hljs-built_in">string</span>};

<span class="hljs-keyword">const</span> posts = [
    {id: <span class="hljs-number">0</span>, title: <span class="hljs-string">"Title 1"</span>, content: <span class="hljs-string">"Content 1"</span>},
    {id: <span class="hljs-number">1</span>, title: <span class="hljs-string">"Title 2"</span>, content: <span class="hljs-string">"Content 2"</span>},
    {id: <span class="hljs-number">2</span>, title: <span class="hljs-string">"Title 3"</span>, content: <span class="hljs-string">"Content 3"</span>},
    <span class="hljs-literal">null</span>,
    <span class="hljs-literal">undefined</span>
];

<span class="hljs-keyword">const</span> filteredPosts = posts.filter((post): post is Post =&gt; post != <span class="hljs-literal">null</span>);
</code></pre>
<p>Now, TypeScript understands <code>filteredPosts</code> as an array containing only <code>Post</code> (i.e. <code>{id: number, title: string, content: string}[]</code>).</p>
<h2 id="heading-improving-reusability">Improving Reusability</h2>
<p>Let's level up our function's versatility using generics and TypeScript's <code>NonNullable</code> utility type:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> posts = [
    {id: <span class="hljs-number">0</span>, title: <span class="hljs-string">"Title 1"</span>, content: <span class="hljs-string">"Content 1"</span>},
    {id: <span class="hljs-number">1</span>, title: <span class="hljs-string">"Title 2"</span>, content: <span class="hljs-string">"Content 2"</span>},
    {id: <span class="hljs-number">2</span>, title: <span class="hljs-string">"Title 3"</span>, content: <span class="hljs-string">"Content 3"</span>},
    <span class="hljs-literal">null</span>,
    <span class="hljs-literal">undefined</span>
];

<span class="hljs-keyword">const</span> chooseNonNullable = &lt;T&gt;(arg: T): arg is NonNullable&lt;T&gt; =&gt; arg != <span class="hljs-literal">null</span>;

<span class="hljs-keyword">const</span> filteredPosts = posts.filter(chooseNonNullable);
</code></pre>
<p>Now, you can export <code>chooseNonNullable</code> and use it as a callback to any <code>filter</code> methods to remove both <code>null</code> and <code>undefined</code>.</p>
<h2 id="heading-addressing-an-alternative">Addressing an Alternative</h2>
<p>You might wonder about using the <code>as</code> keyword instead of type guards. While possible, it's less reliable as it assumes a type without verification, leading to potential errors. Stick with type guards for accuracy and safety!</p>
]]></content:encoded></item></channel></rss>