<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>No More Trash: Smarter Web3 Websites</title>
        <link>https://paragraph.com/@bcryptojake</link>
        <description>The Anti-Garbage Guide to Web3 Websites is a weekly publication that explores how to design and build Web3 products without falling into the traps of clunky UX, pointless token mechanics, and scam-like aesthetics. Each issue breaks down real-world examples, common mistakes, and practical solutions for developers, designers, and founders who want to create Web3 experiences that actually work — and don’t look or feel like trash.</description>
        <lastBuildDate>Mon, 20 Apr 2026 04:38:11 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>No More Trash: Smarter Web3 Websites</title>
            <url>https://storage.googleapis.com/papyrus_images/b1d589377f30533eff345329eb615a97a97f353bd54cfd41bf874528b58dff9b.jpg</url>
            <link>https://paragraph.com/@bcryptojake</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[Goodbye GameFi]]></title>
            <link>https://paragraph.com/@bcryptojake/goodbye-gamefi</link>
            <guid>H6e3vhS7Bf7cWcQR7K8a</guid>
            <pubDate>Tue, 16 Dec 2025 15:41:08 GMT</pubDate>
            <description><![CDATA[지난 2년, 국내 Web3 개발자 커리어의 시작점은 대부분(아마 70% 이상은) GameFi였을 것입니다. 저 역시 2023년 P2E 내러티브가 뜨겁게 타오를 때 이 시장에 뛰어들었고, 최근 그 흐름이 텔레그램 미니앱이나 리워드 앱 형태로 옮겨가는 과정까지 최전선에서 목격했습니다. 아실분들은 아시겠지만, 저는 최근에 곰블에서부터 새로운 팀으로 이직한 상태입니다. 이번 이직에서 제가 결심했던 것 중 하나는 "이번엔 블록체인 게임 프로젝트로는 가지말자!" 였었습니다. 시장이 식어서라기보다는 제가 경험한 구조적 한계와, 그 안에서 느꼈던 개발자 혹은 직원로서의 갈증이 주요 원인인데요. 2025년을 마무리하면서 제가 GameFi를 떠나게 된 이유와, 그럼에도 불구하고 기대하는 이 시장의 미래를 회고해 봅니다.1. '게임'과 '금융' 사이의 위태로운 줄타기대다수의 프로젝트에서 블록체인 기반의 게임 제작이 어려운 이유는 '게임(재미)'과 '금융(수익)'이라는 상충되는 두 가치를 동시에 잡...]]></description>
            <content:encoded><![CDATA[<br><p>지난 2년, 국내 Web3 개발자 커리어의 시작점은 대부분(아마 70% 이상은) <strong>GameFi</strong>였을 것입니다. 저 역시 2023년 P2E 내러티브가 뜨겁게 타오를 때 이 시장에 뛰어들었고, 최근 그 흐름이 텔레그램 미니앱이나 리워드 앱 형태로 옮겨가는 과정까지 최전선에서 목격했습니다.</p><p>아실분들은 아시겠지만, 저는 최근에 곰블에서부터 새로운 팀으로 이직한 상태입니다. 이번 이직에서 제가 결심했던 것 중 하나는 <code>"이번엔 블록체인 게임 프로젝트로는 가지말자!"</code><strong> </strong>였었습니다.</p><p>시장이 식어서라기보다는 제가 경험한 구조적 한계와, 그 안에서 느꼈던 개발자 혹은 직원로서의 갈증이 주요 원인인데요. 2025년을 마무리하면서 제가 GameFi를 떠나게 된 이유와, 그럼에도 불구하고 기대하는 이 시장의 미래를 회고해 봅니다.</p><hr><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/5ef9c8c8aae1717e6bc9a943c7b662eeafdfdebd4138c91caba79de9c9dbcae6.png" blurdataurl="data:image/png;base64,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" nextheight="559" nextwidth="1024" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-1" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1. '게임'과 '금융' 사이의 위태로운 줄타기</h3><p>대다수의 프로젝트에서 블록체인 기반의 게임 제작이 어려운 이유는 <strong>'게임(재미)'</strong>과 <strong>'금융(수익)'</strong>이라는 상충되는 두 가치를 동시에 잡아야 하기 때문입니다.</p><ul><li><p><strong>게임의 본질</strong><br>유저의 몰입과 체류를 만드는데에 집중해야 합니다. 긴 시간 플레이해도 매번 다른 경험을 제공하며 밸런스를 맞춥니다. 일일 보상이나 패스를 판매해서 돈과 시간을 최대한 많이 들이도록 유도합니다.</p></li><li><p><strong>GameFi의 현실</strong><br>크립토 네이티브는 "이걸 얼마나 하면 원금 회수(ROI)가 되는가?"를 먼저 봅니다. 돈이 될거 같으면 빨리 스코어를 달성하고 다계정을 하고, 레퍼럴을 유도하고 싶어합니다.</p></li></ul><p>크립토 유저들은 게임 자체보다 <strong>Backer(투자사)가 누구인지, 예상 시가총액은 얼마이고 상장에서 얼마나 먹여줄건지, 어떤 KOL이 불리쉬(Bullish)하는지</strong>를 더 중요하게 여깁니다.</p><p>이 지점에서 GameFi는 <strong>크립토 네이티브</strong>와 <strong>일반 리테일</strong> 유저 모두를 놓칩니다.</p><ul><li><p><strong>크립토 유저</strong><br>가령 요즘 국내에서 많이들 하시는 Perp-Dex를 예로 들면 주간 거래량을 채우거나(= 여러 거래소에서 봇으로 양빵을 잡기) 수수료만 녹이면 되는데, GameFi는 불확실한 에어드랍을 위해 몇 시간씩 붙잡고 단순 노동을 해야 하니 비효율적입니다.</p></li><li><p><strong>일반 유저</strong><br>'재미'를 기대하고 왔다가, "OO 파쿠리 게임" 혹은 "XX 하위 호환" 과 같은 조약한 퀄리티와 화면을 가득 채운 레퍼럴(Referral) 대시보드에 질려 떠나갑니다. 이건 Web2의 큰 IP를 기반으로 서비스 하려는 시도들에도 마찬가지입니다.</p></li></ul><h3 id="h-2-vc" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2. VC의 압박</h3><p>프로젝트 내부 사정은 더 복잡합니다. 아무리 좋은 Backer가 있어도, VC들은 <strong>'트렌드'</strong>와 <strong>'회수'</strong>를 강요합니다.</p><p>투자사들은 끊임없이 최신 유행을 요구합니다. 과거엔 P2E였고, 최근엔 <code>ERC-402</code>, <code>Eliza</code> 같은 AI Agent, 혹은 특정 체인으로의 마이그레이션이 그 대상입니다. 개발팀은 기존 인력으로 이를 급하게 소화해야 합니다.</p><p>이런 요구사항을 맞춰 프로덕트가 시장에 나올 즈음이면, 이미 수많은 경쟁자가 파이를 나눠 가진 뒤입니다. 결국 수익 창출은 어렵고 프로젝트는 폐기됩니다. 이 과정에서 게임의 형태도 변질되었습니다.</p><h3 id="h-2-1" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2-1. 점점 가벼워지는 게임과 기반 기술들</h3><ul><li><p><strong>과거:</strong> Unity/Unreal 기반의 고퀄리티 PC 클라이언트 게임</p></li><li><p><strong>현재:</strong> 텔레그램/라인 미니앱, 혹은 웹 기반의 '딸깍' 도박장 (Clicker)</p></li></ul><p>이제는 엔진을 다루는 기술보다, <strong>DOM 기반의 클릭커 류 게임</strong>을 빠르게 찍어내는 능력이 더 요구되는 것이 현실입니다.</p><h3 id="h-3" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3.  직원으로써도 성장하지 못하는 반복 작업들 (개발자 시점)</h3><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/74c871c6be49a5bbd6ead21f7cb2ae14bf7161b29fc2bb17a95b24f9c124ae63.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAARCAIAAAAzPjmrAAAACXBIWXMAAAsTAAALEwEAmpwYAAADfUlEQVR4nJ2UIYi0TBjHDVuuXjg4LhwsCMLBhYULwsI2wbBgWNhgGDAIBsMEwWAQDBMmGIQtAxMuGAwbNmwwbNgwwWAwGAwTDAbDFmHLhfs458Xv7t7lC98viM7MM39nnv/zSHVdc87bkW6kHmlGqm+IkWm2ruubg7+QmqbJ85wQQind7XaEEBHGGMMYx3EcRRFCKEkSCCFjrOu6tm3DMEQIYYwhhJ7nRVEEIaSUcs6rqvohwBjzfX83kqap4zhpmrZtizFWVVWWZYzx29vbZoQQwhjzPM+27fl8vl6vLcuaz+dPT0/b7RZj3Lbtb4E8z5MkwRhLkgQAqKpK13XXdSVJghDKsqxpmmVZq9XKdV0AAEJIlmXf9wkhhmEsFovtdvv+/k4IQQjdEDifzxDCMAwBAGEYRiN93x8Oh81mI8uyZVmGYTw+PmqaFobh5XIpisK2bc65+I/X19fj8Ygx9n2/67rfAnVdp2l6OByWy6WiKFmWFUUhcrBer8UVu64LIQyC4HA4cM7LskySJI5jkZvT6bTf73e73el0+jvPXwKc82EYsizzPO/j42Oa++6ielw2xQvjlWWp67phGLZtl2XJORezU0hVVV8C4oMxBiEUm4rd+YhwsHgRW4ipvu8ZY5qm2bZtmmZRFFMCpqg/JxAaXdeFYZgkSd/3dV2fz+fDSJZlaZru93vxrOu6KIp0JAiC2Wx2HknTNM9zccQoilarled5f04wwTm3LItSer1eoyiSZXm5XGqaZpqmruuqqkIIh2HAGAMAkiQBAARB4Ps+AMAwDAjh5XKhlCKETNO0LIsQ8kNA6DuOE0WR4zi6ri8WC1VVDcN4eXmxLEsY33Xd+/v7+XwOIVRVVdO01WqlKIrv+8MwcM4BALZtu657PB5/CAjats3zXOStaRpZlmezWZZln5+fIqWu61ZVhTE2TdMwjO12Ky4ZYxxF0fPzs6Zpu91OhN8QqKqq73vRBhRFsW377u5uNpuZpum67vV69X0fY+w4jjTy8PCAEBIFtNlsJEkKgmAYhn9d9Decc0opAMA0TcdxRCUGQUAprev6eDwmSUII2Y+kaUopjeOYMZZl2WKx+N6RbgtM3bQZs9I0jbCdcB7nXLQ8YdzJlH3fI4Qk6auB/ii0m7v/qvibTGuqqirLsuu6OI4VRZnK5b9O8D/gnIviEOkVAv8AJKIxPltlSc0AAAAASUVORK5CYII=" nextheight="559" nextwidth="1024" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>직원, 특히 개발자 입장에서 GameFi는 <strong>'무한한 택갈이(Reskinning)의 굴레'</strong>처럼 느껴지기도 합니다.</p><ol><li><p>NFT 유틸리티 연결</p></li><li><p>리더보드 및 보상 분배</p></li><li><p>Off-chain 포인트의 On-chain 환전(Redeem)</p></li><li><p>오라클 RNG를 활용한 가챠/카지노 로직</p></li><li><p>Daily 퀘스트 및 출석 체크</p></li></ol><p>지난 3년간 저는 이 로직들을 수없이 반복해서 짰습니다.<br>이 지점에서 주변 동료들이 많이 힘들어하는 걸 봤습니다. 기본적으로 Web3로 온 개발자들이 계속 Web3 시장에 체류하는 경우가 드물기 때문입니다. 같은 연차에서 고민하는 영역과 너무 다른 고민들과 점점 벌어지는 도메인 지식에서 많은 불안함을 느낍니다.</p><br><p>제가 GameFi를 떠난 가장 큰 이유는 <strong>시야의 확장</strong>을 위해서입니다. 계속 만들어왔던 "그 기능들"(리더보드를 만들고 토큰을 정산해 주는 역할)을 넘어, <strong>"이 프로덕트가 왜 필요한가?", "어떤 문제를 해결하는가?"</strong>라는 본질적인 고민을 하고 싶었습니다. 같은 기능을 만들어도 더 넓은 시장에서, 실제 유저의 삶에 임팩트를 주는 서비스를 만들고 싶다는 갈망이 저를 새로운 도전으로 이끌었습니다.</p><h3 id="h-4-gamefi" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">4. 그럼에도 불구하고, GameFi의 미래는 있다</h3><p>비록 저는 떠나지만, 이 산업이 가치 없다고 생각하지는 않습니다. 오히려 GameFi는 <strong>'가장 강력한 Web3 튜토리얼'</strong>이 될 잠재력이 있습니다.</p><p>미래의 GameFi는 국내보단 해외에서, 그리고 투기보다는 <strong>'교육과 경험'</strong>의 측면에서 빛을 발할 것이라 생각합니다. "NFT 가격이 90% 폭락했다"는 뉴스보다, 게임을 통해 <strong>"NFT가 내 아이템을 소유하게 해 주는구나"</strong>라는 효능감을 직관적으로 느끼게 해 줄 수 있는 건 여전히 게임뿐이기 때문입니다.</p><p>금융의 껍데기를 쓴 게임이 아니라, 블록체인의 복잡성을 숨기고 <strong>'실력 기반 보상'</strong>과 <strong>'진정한 소유권'</strong>을 가르쳐주는 웰메이드 프로젝트들이 이 시장의 다음 챕터를 열어주길 기대합니다.</p><br><blockquote><p>이 글은 작성자의 기본적인 초안을 바탕으로 Gemini 3.0의 도움을 받아 작성되었음을 알립니다.</p></blockquote><br>]]></content:encoded>
            <author>bcryptojake@newsletter.paragraph.com (CryptoJake.eth)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/8c77b20b2f13a7e447c0e0cbd0fa3cf4944a6584061a9a81277e71bd299b85d2.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Why Every TGE Website Feels Like BULLSHIT]]></title>
            <link>https://paragraph.com/@bcryptojake/why-every-tge-website-feels-like-bullshit</link>
            <guid>YTvTMzL3mdrtmdukWJ5B</guid>
            <pubDate>Wed, 01 Oct 2025 09:29:41 GMT</pubDate>
            <description><![CDATA[Hello It's @b_cryptojake. I’ve been developing DApps for about 3 years now. As of this writing (2025.10.01), a service called Legion and its YieldBasis deposit feature has been quite popular in Korean TG chats. The patterns of depositing stablecoins and yield farming are consistent with the recent low-risk DeFi trend that Vitalik often talks about, so I think it’s an interesting field to explore.TG(KR) - 돈 못버는 방 At the launch of most TGE sites or deposit services that attract a lot of attenti...]]></description>
            <content:encoded><![CDATA[<p>Hello It's @b_cryptojake.</p><p>I’ve been developing DApps for about 3 years now. As of this writing (2025.10.01), a service called <em>Legion</em> and its <em>YieldBasis deposit</em> feature has been quite popular in Korean TG chats. The patterns of depositing stablecoins and yield farming are consistent with the recent low-risk DeFi trend that Vitalik often talks about, so I think it’s an interesting field to explore.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/5c5b6b23624033e90f31cf1ca4ce7b07f22977cf84f53f44a6fc0a4b49c742b3.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAgCAIAAABywqTfAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFGUlEQVR4nIWVb0wTZxjA383sw5Ys7svMZjK3VLswFp3iUpYqnaJOhGT1Q6eYWbdks2MdxjmWpSYWrX+GUhWUovzZ7OYZQd2J3OQqlmrPtVeys/RaKG4cmgv0rFTWK4ETegVu4W7UUnH+0jTXS3/3PO/zvs9zAG24AAAoKtZrtVoEQQRB4Hle+F/+9AeHR7j+8IPhEQ6EmLDH5YJhGIIggiAkn+d5juMiIgzDsCwr3eR5PpFItHcEBqNsNBqLRqPgycdL8Q+XlYFpSkpK0vKSrvV6/ZSfSCR4nh9PjPeH+l1u9917d+t/tiZlDMNGH43yfELSotHoxOQkx3GCIKjV6hnx/3DjhZ9u3aL97Lsfdr/97tKMJcs3frIFvoJcbr56Eb5SWXXqhOU0x3HSQgRB0Gg0M3yapi9eutTUjKBoq9vt6Q+FGOa+j/R7faSP9N++3UEQ3kgkwvMJnh9L9/vCEcPhShRFW1D0uv16C4rSNM2lwPP8yMiwt6OD4x7NEv/+w8GymjOVVaeMe/daz54djLLhgUhfiOkLMaEQQ929R3T4XO3tos/Nnn94IHLIXFm6bx/c1JS2KSwba2i4cKLqJMMwgiBI9dOk+hOTEw8GBoZiMZZlR+OjY/F4csul4zAWjw/FhsT1P6V+XcFur48kO7v8XXeampGTFgt0vqGyytLUjAS6gl4f6fWRf/dQgiCMxeOCIBQWFs7wKarX5Xa73R6n85bb7bnpdDocjhYUtdvtDofjptNpt9spikrmX1BQMMOHIEgul6tUKpPJpFQq6+rqGIahKIpMQfIlMAyb4d9wOLZptQf2m77Sbd9RXHz8qLm01HikrCzgJ7sC/jvdXd7bBEmSqQpI/dEg9uL8N2UvvDR3YdbKN7Jynp8vfzVTsW7TF4r8wg/UW49Z6nt7e5ONwPP8f/7E5KQgCNVnzr+1Ml+2suC1rFULsj8q3nPIcOCo4eDx7/eX7/7x6NcG00HzyZ6eHqkXZol/8Td4zotzFyxcpFqzfm1e/l89PXe6g9In4PeL32QwGHxq/jccjnnz5j0HwNo1udnZ2QqFoqCgYNPmzTZbq0cEx/Fn+B/n5edt2JCbm6tUKnU6HYIgNpsNx3FimmAwmDoIgFiGxHhiXBCE1t9bZHNe/vX8pT6apiiqbRoMwxpFrFYrhmEz4k9MVW6qeCKTQ8PDQ7EYTdMqlUomky1evFgulysUCpIkpYkmnZzH/sjoWHco/M9gtIPodN0kBh8+lP5nNBr1er3BYDAajWazmWXZVO2xv2K7AQCw++CxrMz3s5fm7Pp2l07EZrNhGJZWrVl8pM3z+rIVZCDwk9V6trFhav5cvgzDMIZhbW1tNE0nu212/xjaBDauPlRTo1y9bumHq7brd5SWGk0mk81mg2E4bbWz+K1e4pUvP3e63HXVtTWWWgRBIAiCYTgoIo0ajuNYkScfB6TD2HL1WuairHdky/X6b0wmU0lJyc6dO6VC6HQ6KR0IgtI2TxAEcM5+C2TmoA5neXlZ9enqI+XlFoulsbEREpG2XSoEQRA0Taf7a4r2AABO/XLOUlHReu1auwdP7fBnAsbi8X76HtnZaa6t319xwlRRBQCor6vFcTx5/iRwHMcwTJqfydckUL23BACgLyrarN2Wsyp3mSI7IyMjb/16rVar0Wi0Wm3hNGq1WqPRSPOD53npvQpgCAIAkCQ5EA730XT4/tRdWjz/FEXRNM1MQ5KkFF+KHIlEWJb9F0QYCY9gKd8UAAAAAElFTkSuQmCC" nextheight="1622" nextwidth="1072" class="image-node embed"><figcaption htmlattributes="[object Object]" class="">TG(KR) - <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://t.me/cryptopangpang/2997">돈 못버는 방</a></figcaption></figure><br><p>At the launch of most TGE sites or deposit services that attract a lot of attention, the sites are usually unstable. <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://yieldbasis.com/">Legion</a> is no exception. One user said they clicked for <strong>1 hour and 40 minutes</strong> but still couldn’t even trigger the <code>approve()</code> function.</p><p>It’s easy to assume that the main resource of a DApp team is smart contract development, so problems like this must be chain-related. But in most cases, it’s actually a <strong>front-end RPC request optimization issue</strong>.</p><p>So, I’ll share some tips on how to make your service more reliable during traffic spikes (<em>temporary surges in transactions or view requests</em>). – There’s also a Wagmi-based code snippet included!</p><h2 id="h-the-nightmare-of-zero-day" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Nightmare of Zero Day</h2><hr><p>Let’s talk about the <em>Broken RPC situation</em>, which most builders and users have probably experienced.</p><ul><li><p><strong>100K+ people flooding in within 10 minutes</strong></p></li><li><p>Everyone pressing the same button (“Mint”, “Buy”, “Claim”, “Deposit”)</p></li><li><p>Multiple functions on the page trying to read variables from the contract</p></li></ul><p>In these situations, the root cause is often <strong>frontend and RPC management</strong>, not the blockchain itself. Here are some common workarounds:</p><ul><li><p>Quickly spin up a private node (Infura, QuickNode, Alchemy) and replace your RPC.<br><em>(But the 100M request cap can be filled in just 3 hours.)</em></p></li><li><p>After an announcement (ANN), deploy optimizations as fast as possible.</p></li><li><p>Even then, if you rely only on frontend cache invalidation, users won’t see fixes unless they refresh, so the damage may already be done.</p></li></ul><h2 id="h-checklist-check-your-stability" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Checklist: Check your stability</h2><hr><p>Here’s a checklist you can follow on the frontend side:</p><ol><li><p><span data-name="check_mark_button" class="emoji" data-type="emoji">✅</span> RPC Fallback</p><p>Do you have a backup RPC to switch to when the main one fails?<br>If an RPC breaks and keeps throwing errors without responding, your code should automatically fall back to another RPC.</p><h3 id="h-frontend-caching" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><span data-name="check_mark_button" class="emoji" data-type="emoji">✅</span> Frontend Caching</h3><p>Most Wagmi hooks use <code>@tanstack/react-query</code> for caching by default.<br>By caching view function requests, you can reduce the risk of hitting RPC rate limits.</p><h3 id="h-multicall" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><span data-name="check_mark_button" class="emoji" data-type="emoji">✅</span> Multicall</h3><p>If you’re calling multiple view functions from the same contract, bundle them into a single <code>multicall()</code> request. This reduces traffic and avoids rate limits.</p><h3 id="h-ssr-caching-if-using-ssr" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><span data-name="check_mark_button" class="emoji" data-type="emoji">✅</span> SSR Caching (if using SSR)</h3><p>If you’re regenerating pages from the server every time without caching, it can slow things down significantly. Regional caching of SSR responses improves load times.</p><h3 id="h-pre-warm-and-test" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><span data-name="check_mark_button" class="emoji" data-type="emoji">✅</span> Pre-warm and Test</h3><p>Use load testing tools (e.g. k6, Artillery) before launch to simulate traffic spikes.</p><p><em>(Off the record: I used to monitor service communities like Discord during zero-day launches. The result? Every post spammed with the </em><span data-name="rotating_light" class="emoji" data-type="emoji">🚨</span><em>SCAM emoji and the CEO raging. Not an experience I want to repeat.)</em></p></li></ol><h2 id="h-example1-fallback-rpcs" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Example1: Fallback RPCs</h2><hr><h3 id="h-simple-shuffle-your-rpcs" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Simple Shuffle your RPCs</h3><p>Here’s a simple way to shuffle RPCs and create a fallback setup with Wagmi:</p><pre data-type="codeBlock" text="// the target chain: binance smart chain
import { bsc } from 'wagmi/chains';
import { createConfig } from 'wagmi';

export const SUPPORTED_CHAIN = bsc;

const PUBLIC_RPC = [
  '&lt;https://bsc-dataseed3.binance.org&gt;',
  '&lt;https://bsc-dataseed4.binance.org&gt;',
  '&lt;https://bsc-dataseed1.defibit.io&gt;',
  '&lt;https://bsc-dataseed2.defibit.io&gt;',
  '&lt;https://bsc-dataseed3.defibit.io&gt;',
  '&lt;https://bsc-dataseed4.defibit.io&gt;',
  '&lt;https://bsc-dataseed1.ninicoin.io&gt;',
  '&lt;https://bsc-dataseed2.ninicoin.io&gt;',
  '&lt;https://bsc-dataseed3.ninicoin.io&gt;',
  '&lt;https://bsc-dataseed4.ninicoin.io&gt;',
];

function shuffleRPCList() {
  const shuffledList = [...PUBLIC_RPC];
  for (let i = shuffledList.length - 1; i &gt; 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [shuffledList[i], shuffledList[j]] = [shuffledList[j], shuffledList[i]]
  }

  return shuffledList
}

const createFallbackTransport = (timeout = 7000) =&gt; {
  const transports = shuffleRPCList().map((url) =&gt; http(url, { timeout }))

  return fallback(transports, {
    retryCount: PUBLIC_RPC.length,
    rank: false,
  })
};

export const config = createConfig({
  chains: [SUPPORT_CHAIN],
  transports: { [bsc.id]: createFallbackTransport() },
});
"><code><span class="hljs-comment">// the target chain: binance smart chain</span>
<span class="hljs-keyword">import</span> { <span class="hljs-title">bsc</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'wagmi/chains'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">createConfig</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'wagmi'</span>;

export const SUPPORTED_CHAIN <span class="hljs-operator">=</span> bsc;

const PUBLIC_RPC <span class="hljs-operator">=</span> [
  <span class="hljs-string">'&lt;https://bsc-dataseed3.binance.org&gt;'</span>,
  <span class="hljs-string">'&lt;https://bsc-dataseed4.binance.org&gt;'</span>,
  <span class="hljs-string">'&lt;https://bsc-dataseed1.defibit.io&gt;'</span>,
  <span class="hljs-string">'&lt;https://bsc-dataseed2.defibit.io&gt;'</span>,
  <span class="hljs-string">'&lt;https://bsc-dataseed3.defibit.io&gt;'</span>,
  <span class="hljs-string">'&lt;https://bsc-dataseed4.defibit.io&gt;'</span>,
  <span class="hljs-string">'&lt;https://bsc-dataseed1.ninicoin.io&gt;'</span>,
  <span class="hljs-string">'&lt;https://bsc-dataseed2.ninicoin.io&gt;'</span>,
  <span class="hljs-string">'&lt;https://bsc-dataseed3.ninicoin.io&gt;'</span>,
  <span class="hljs-string">'&lt;https://bsc-dataseed4.ninicoin.io&gt;'</span>,
];

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">shuffleRPCList</span>(<span class="hljs-params"></span>) </span>{
  const shuffledList <span class="hljs-operator">=</span> [...PUBLIC_RPC];
  <span class="hljs-keyword">for</span> (let i <span class="hljs-operator">=</span> shuffledList.<span class="hljs-built_in">length</span> <span class="hljs-operator">-</span> <span class="hljs-number">1</span>; i <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span>; i<span class="hljs-operator">-</span><span class="hljs-operator">-</span>) {
    const j <span class="hljs-operator">=</span> Math.floor(Math.random() <span class="hljs-operator">*</span> (i <span class="hljs-operator">+</span> <span class="hljs-number">1</span>));
    [shuffledList[i], shuffledList[j]] <span class="hljs-operator">=</span> [shuffledList[j], shuffledList[i]]
  }

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

const createFallbackTransport <span class="hljs-operator">=</span> (timeout <span class="hljs-operator">=</span> <span class="hljs-number">7000</span>) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  const transports <span class="hljs-operator">=</span> shuffleRPCList().map((url) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> http(url, { timeout }))

  <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">fallback</span>(<span class="hljs-params">transports, {
    retryCount: PUBLIC_RPC.length,
    rank: <span class="hljs-literal">false</span>,
  }</span>)
}</span>;

export const config <span class="hljs-operator">=</span> createConfig({
  chains: [SUPPORT_CHAIN],
  transports: { [bsc.id]: createFallbackTransport() },
});
</code></pre><p><span data-name="point_right" class="emoji" data-type="emoji">👉</span> <code>fallback()</code> (provided by Viem) retries failed RPCs according to <code>retryCount</code>, and you can decide whether to rank them or call sequentially.</p><h3 id="h-good-to-know-how-to-check-your-blockchain-stability" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">(Good to know) How to check your blockchain stability</h3><hr><p>As some of you may know, there is a website with a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ct.app/">great utility called CoinTool</a>.</p><p>You can use it to get an idea of whether our service's network will suddenly spike in gas prices, or whether it will provide relatively stable traffic.</p><p>For example, my example BSC employs <strong>Proof of Staked Authority (PoSA)</strong>, so it has a high TPS, and the gas fee is always fixed at 1 Gwei, so you can see that it's not going to be unexpected!</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/7ac9f63cc64f6587d09b96ffa5f2081270e9f011683d5f6761885de7f0b15034.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAACXBIWXMAAAsTAAALEwEAmpwYAAAIOElEQVR4nH1UC1BU5xU+Nq9a05qYaaMiu/ISFlTEifGFnTrGam1FaSzV+EYlKhIIKi4JqDwUzTg+ItVCEkGJwYpKUAaLClKikbDsg5V9sCyr7i57977v3V0WNYnT+f/rKkuazpy58//nnnO+c75zzg+cIHCCwAti2z3TjZ6OWzZds1Xd4TAM+L39/R5OEHlR5EWP6H9sFwUD5+7iSdLnF0QvLwicKPo8vgGf/5n4vT4UDbkg4QQBKIYlGY5kOFEQLffF7wycxsx3mLj/3CU7rRQv8ATDkaSbtKhd3e19WAiLiiJJNyswDNvpvH/Namy2mZts5us2k8puY1mBYlhJCIoGmuVoliNZjuM42K6Ghc2w/Bv4+zewoAlyNGZLL+t7RKiv6TaDPuMlw64IfcZL2o3Qd72M4DwPRS/UHodDK6BsG5Ijq6FCabbbpZg0y1EMGwBgOEEUIEMFv2+A+Q2wsBHmNkK2+kj1dw7WT3U0qFfCnfegbTXcSQVt2ovOmt12VeOA/wc4VwL5f4Hi5VDyHhSlQOVH++809rj6OI4fCuDxirC1HZKvQkbryqNa2NQCH6rDlbdMfT5OU9+eCp37FmoL5hnLN2t2TbZXpLlazz569CNUFcHhNPjnTqgqhnIlnNq1+s7Xasc9kRfJ/1HBByqU/vpm2NACS66BUvvJOXUv6WW011Wbhqt2TulQTlPlvq3NCuurLXa2N6AKLh6GA2vgH1lwLB0OrIOqvQdUTSaXc2gFNMvxAg85GljcAhvuwPpv0WGHpr6p3c37SV2TPvMNozKqp2SeMVfRtSPE2XjSYdb4vQPj6k9A6SaoykNyYgtUF9eobzspimGFYIpYNF2Qq0Vx196G9a2wrBWy1VqDlfU9IrU3urLH6HOiDIVzu/KnG3JkroZSguX9vCe88XMoTYPPd0KFEso+gC93t9ssLMeTQ5qMhUFNXtAESS2Q0gqLmmGHhuNY2jPgbr/cmf6LzvRXDMpIU36cPvMV9+0ayvfYy3FQ9yl8ug7K0qFyJ5Rthap8lhUYnPFzAIJm/H7v5WYdzCqA6YUwoxBmFaFDYsGVmzrfw4fmU5nqLb/szAzRZYbos8M1W35lKF3n8fhvWQywfzMUroGidVC8AYpTYc+qM6rWh76HBM0MqYB1kXQfQbhJt5t0E/jrJAgXSaNNJN00wxAUTTEMxTA0y9IMotlF0U63m6BogqLcJE1SyIagaeane8CLHl+/3+Pt93oHvN4Bj7ff4/H70LlfEH284NPpDHe7TBpdl8FoUat13T33nht7fF7vAC948LLyz6I/B3CTtKG3p8Ok01tNHSZdh0mntdzVWu5KZ73VqDHr2/Sq9i612qRr61J/q1O1GzQ6iyFggLxsTgcdeCSCAJwEYet1TNs6FyIBZgBEYUkAiA+cZwAoAP1VAEwLKKdig0iACQCzASKg8PTBJ4+fuEiaZPihAL1Wu2Ld2zAOx5UBhALEAEQD0oTiQOEAcqyZhmIFGcixVwh8/EXxkx+eEBRN4w0YBOAieiwPJm6YiUJPBRiPfWJxvnJ8xcr4tMSDX5UWf3k0qzQPJR6DDWQY+y10yP9i34/f/0wFtl7HU4CEAEAMFjlAGFZGYokHSByG4o7H0WMwgBzrQxAAroBjWM+QCiirxT5pw2zk9hbOSPJXYCrG4GrkAL/F1wkAowMlxmDLKIBExNueiv3+/u97nU6nm6QZURKS4sHhJG1W57gVk2AUpvVNnFcsPkcB/OHlYUtlMG8Eyn3ea5AUCokvoLLipL/D91YeLjyNRL56Kswf+UJyGCwOMfX28nw/yfAIoI+gzSbbx58VzclelFK4NjFr4dOBQakNyynfu6di/46Tu9OPKjNK83LLS9YfzEK/pBLDcB4zcUKh+BqJHDu7TQLvJykJwEWbjLas0tyYTTPm5yTFbJz+vIcSRXGYijHYPxbRjViKwuM0BtM1GRMrw2BxyEbfYw4CsFrs72xfipq5YBSambBAglKTp+K85DhoPI4750VIkiGRhiIBK+U/B0DQRqO1rPZMZmluQeUn6Ud2oE5GDxrTBOwvwwCTENia/e8rEXUlE9MSkT5+UDURSIIouu9w9XQ/CF+VgNobCzA2eA/kgwCkSZXoekaRVOJ4iNs4e09FSfaJPGV5wQOnCw2rNEUSABpTOTYNw0EV/xdg/KAMwrFXBBrxl5PDRyyLePnd8KApuu9w26zOoEUb7C8PBpgc2MRniyIDmIhhRgWql4Ou2yQIzyiyu3q67XGpg56K0U+pRAyMD+6BVIHEYTTWxwP88TcwGWZlLCisPKQsL8j/bN/9PmIoRbESQAKmaP6o11NiXktRoAGXBQNIBnI8LZEAc0cUnTlcXHW0uOqobPUUmPcq2so/jw2iyO5wW3sc8ZsCPYiAJbvXbjuWl32ySLFxzlNlFH44pfGVFkriMBTrYwOdj3j6mHd2DxrTew+cZqMtdMVk9Noo8FMxPOD5Kr4qEGnD343edixv82HlX3enol/SZkyF1IPb3z+yK+2QEr3koRggEnQmI8/73CTrJjngOC9NiqmHMmDhG/K1CZAsg2TZyJTo362Kg+RQSBo9bs0UWCaP2jh97vakpL0rZ334J1gSOmJZxIi/RY5MiZ6ZsWDRRynv7Fr6+vIYWDz21ykT3lypsNqdXu9jTujnhH5ounmz6ebNG80tNTV1jY1Nt2+r/n21qbauvrW1rbW1ra7u6pX6RqS83lxx5qtLlxtuNLd8XddQefrspUtXzl+sPX7i5PET5afPVldXXygvP1V97kJtXf35ixf/VXOh+nxN9fnz/wXpjr4vchKjAQAAAABJRU5ErkJggg==" nextheight="1484" nextwidth="1472" class="image-node embed"><figcaption htmlattributes="[object Object]" class="">The gas fee history of BSC</figcaption></figure><p>If Binanace Alpha is confirmed at TGE, there's no problem with the above code because the BSC chain uses BEP-20.</p><p>On the other hand, let's look at the Linea chain I built in the past.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/166be769ec35158cc5db4f78e73d95c84ee76a23150ff7f81b79351b04d349ef.png" blurdataurl="data:image/png;base64,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" nextheight="1484" nextwidth="1472" class="image-node embed"><figcaption htmlattributes="[object Object]" class="">The gas fee history of Linea</figcaption></figure><p>Linea has been relatively stable in the recent past, but gas prices tend to spike at certain times. If your recent history is choppy, you might want to consider more RPC-optimized logic.</p><h2 id="h-example2-multicall-in-wagmi" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Example2: Multicall in Wagmi</h2><hr><p>All RPCs support multicall. With Wagmi, you can easily batch view functions.</p><p>Here’s my <code>useBalance</code> hook:</p><pre data-type="codeBlock" text="// /src/service/useBalance.ts

import { useReadContracts } from 'wagmi'
import { erc20Abi, Address } from 'viem'

export function useBalance(tokenAddress: Address, walletAddress: Address) {
  const { data, isLoading, isError } = useReadContracts({
    contracts: [
      {
        address: tokenAddress,
        abi: erc20Abi,
        functionName: 'balanceOf',
        args: [walletAddress],
      },
      {
        address: tokenAddress,
        abi: erc20Abi,
        functionName: 'symbol',
      },
      {
        address: tokenAddress,
        abi: erc20Abi,
        functionName: 'decimals',
      },
    ],
    query: {
      enabled: !!tokenAddress &amp;&amp; !!walletAddress,
    },
  })

  if (!data) return { data: null, isLoading, isError }

  const [balanceResult, symbolResult, decimalsResult] = data

  const balanceBigInt = balanceResult?.result as bigint
  const symbol = symbolResult?.result as string
  const decimals = Number(decimalsResult?.result ?? 18)

  const formatted =
    balanceBigInt !== undefined
      ? (Number(balanceBigInt) / Math.pow(10, decimals)).toLocaleString('en-US', {
          minimumFractionDigits: 2,
          maximumFractionDigits: 4,
        })
      : '0.00'

  return {
    data: {
      value: balanceBigInt,
      formatted,
      symbol,
      decimals,
    },
    isLoading,
    isError,
  }
}

"><code><span class="hljs-comment">// /src/service/useBalance.ts</span>

<span class="hljs-keyword">import</span> { <span class="hljs-title">useReadContracts</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'wagmi'</span>
<span class="hljs-title"><span class="hljs-keyword">import</span></span> { <span class="hljs-title">erc20Abi</span>, <span class="hljs-title">Address</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'viem'</span>

<span class="hljs-title">export</span> <span class="hljs-title"><span class="hljs-keyword">function</span></span> <span class="hljs-title">useBalance</span>(<span class="hljs-title">tokenAddress</span>: <span class="hljs-title">Address</span>, <span class="hljs-title">walletAddress</span>: <span class="hljs-title">Address</span>) {
  <span class="hljs-title">const</span> { <span class="hljs-title">data</span>, <span class="hljs-title">isLoading</span>, <span class="hljs-title">isError</span> } <span class="hljs-operator">=</span> <span class="hljs-title">useReadContracts</span>({
    <span class="hljs-title">contracts</span>: [
      {
        <span class="hljs-title"><span class="hljs-keyword">address</span></span>: <span class="hljs-title">tokenAddress</span>,
        <span class="hljs-title"><span class="hljs-built_in">abi</span></span>: <span class="hljs-title">erc20Abi</span>,
        <span class="hljs-title">functionName</span>: <span class="hljs-string">'balanceOf'</span>,
        <span class="hljs-title">args</span>: [<span class="hljs-title">walletAddress</span>],
      },
      {
        <span class="hljs-title"><span class="hljs-keyword">address</span></span>: <span class="hljs-title">tokenAddress</span>,
        <span class="hljs-title"><span class="hljs-built_in">abi</span></span>: <span class="hljs-title">erc20Abi</span>,
        <span class="hljs-title">functionName</span>: <span class="hljs-string">'symbol'</span>,
      },
      {
        <span class="hljs-title"><span class="hljs-keyword">address</span></span>: <span class="hljs-title">tokenAddress</span>,
        <span class="hljs-title"><span class="hljs-built_in">abi</span></span>: <span class="hljs-title">erc20Abi</span>,
        <span class="hljs-title">functionName</span>: <span class="hljs-string">'decimals'</span>,
      },
    ],
    <span class="hljs-title">query</span>: {
      <span class="hljs-title">enabled</span>: <span class="hljs-operator">!</span><span class="hljs-operator">!</span><span class="hljs-title">tokenAddress</span> <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> <span class="hljs-operator">!</span><span class="hljs-operator">!</span><span class="hljs-title">walletAddress</span>,
    },
  })

  <span class="hljs-title"><span class="hljs-keyword">if</span></span> (<span class="hljs-operator">!</span><span class="hljs-title">data</span>) <span class="hljs-title"><span class="hljs-keyword">return</span></span> { <span class="hljs-title">data</span>: <span class="hljs-title">null</span>, <span class="hljs-title">isLoading</span>, <span class="hljs-title">isError</span> }

  <span class="hljs-title">const</span> [<span class="hljs-title">balanceResult</span>, <span class="hljs-title">symbolResult</span>, <span class="hljs-title">decimalsResult</span>] <span class="hljs-operator">=</span> <span class="hljs-title">data</span>

  <span class="hljs-title">const</span> <span class="hljs-title">balanceBigInt</span> <span class="hljs-operator">=</span> <span class="hljs-title">balanceResult</span>?.<span class="hljs-title">result</span> <span class="hljs-title"><span class="hljs-keyword">as</span></span> <span class="hljs-title">bigint</span>
  <span class="hljs-title">const</span> <span class="hljs-title">symbol</span> <span class="hljs-operator">=</span> <span class="hljs-title">symbolResult</span>?.<span class="hljs-title">result</span> <span class="hljs-title"><span class="hljs-keyword">as</span></span> <span class="hljs-title"><span class="hljs-keyword">string</span></span>
  <span class="hljs-title">const</span> <span class="hljs-title">decimals</span> <span class="hljs-operator">=</span> <span class="hljs-title">Number</span>(<span class="hljs-title">decimalsResult</span>?.<span class="hljs-title">result</span> ?? 18)

  <span class="hljs-title">const</span> <span class="hljs-title">formatted</span> <span class="hljs-operator">=</span>
    <span class="hljs-title">balanceBigInt</span> <span class="hljs-operator">!</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-title">undefined</span>
      ? (<span class="hljs-title">Number</span>(<span class="hljs-title">balanceBigInt</span>) <span class="hljs-operator">/</span> <span class="hljs-title">Math</span>.<span class="hljs-title">pow</span>(10, <span class="hljs-title">decimals</span>)).<span class="hljs-title">toLocaleString</span>(<span class="hljs-string">'en-US'</span>, {
          <span class="hljs-title">minimumFractionDigits</span>: 2,
          <span class="hljs-title">maximumFractionDigits</span>: 4,
        })
      : <span class="hljs-string">'0.00'</span>

  <span class="hljs-title"><span class="hljs-keyword">return</span></span> {
    <span class="hljs-title">data</span>: {
      <span class="hljs-title">value</span>: <span class="hljs-title">balanceBigInt</span>,
      <span class="hljs-title">formatted</span>,
      <span class="hljs-title">symbol</span>,
      <span class="hljs-title">decimals</span>,
    },
    <span class="hljs-title">isLoading</span>,
    <span class="hljs-title">isError</span>,
  }
}

</code></pre><p>Instead of making 3 separate calls for <code>balanceOf</code>, <code>symbol</code>, and <code>decimals</code>, this approach batches them into one request. This is especially useful in DeFi apps where polling is frequent (e.g., price quotes every second).</p><hr><h2 id="h-wrapping-up" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Wrapping Up</h2><p>So you can see why most TGE sites (or service launches) feel like bullshit.</p><br><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/7cb986b3cdf2b1440802c8e06e54e58076fe696ee23ef3a09782d372a025275e.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAARCAIAAAAzPjmrAAAACXBIWXMAAAsTAAALEwEAmpwYAAADG0lEQVR4nKWVTUzUQBTHR40cMCaexIvhoAeiEpUla1BjwIPVw4KaoomV6IrGrbh1EdiVzazRitGKRgomEwzQGKB+UePHeHH0QvHSxGSzenCDh/oVu14sauhyGsMWVj5WD8svk+al6cz//WfevAJVVTHGhBBN09w4C8mg67qqqrZt07wAqqqKosiybCAQ8PmqCSHzP9J13bKsPAUQQpRS0zRt27Ysy7ZtZzaUUozxQgXchf6FpmlZgTny7kTHcUzTtCzLnI1t25MCjuNomuZuOsbYjbNvEomEqqr/d2AYBiHEfbqLYIx1XccYA0VRTNM0DANC6PNVsyzLcRzDMD5fNcdxgUBAlmWEkGmabqbPn94Zutd5t//G0P2uu/03k+/fuYeUswoIIZMOkslkPB4X5yFJkjzN6OgopfSj+aG2BjTzoPVMQTS07OhB0HW9IbPQi2wGMwX+OkgkEgihbOIMw7As6/f7BUGY6SBlfTlyAIitK/buAeFTS5t4cK3t+Pi409fX69YIQkiSJCXDlIPIuUirGA3Hwtfb2zs6bkIYhTAaCYchjLombnd3y7KsadrIyMjVq1cO1IDzzYVFAByuBbGmpeHG/S9f6cHgaTuDqqoIIS3DlMAUi4B4uY3ngw3BUFBoPMTVHfXXN7eEY7ELvX0KQujd27fp9EQ8/qauFlw4t6j9UtGV8ytP14PO9pOU0tcjuS/K5Bbt2F0JVoGC4sITx7kKb6mnrKTCW7qz0ustX+cpK9m9a3tLE48Q+vzpE6X059iPhnrPqWNrzvIbzvKlfm71g4EOSunw8HDOQ9Z1ffKQv31NPdIebtkECgBYOW0JAFC8BKxdDsrXg250K5X67s4ZG/s1c6TTEzPL1EXPYBiGoiigp7fn5+9fT54+vgj3hfiKEL/Vs3ExAGDzBtDIbwvxFbFI9eDgQFYgJ47j2P9g6qLNaUHzq83Ku1UoiuI4DsZ4aOiBKF6EMCoIQXdAGO3qlDF+hjHOv5tijN0cZVl27wHLslVVVQzDcBwnCIIkSYSQBbVrQkjLNIIgZGMIoSiKsiwv5H/wB+bN0Kjcr/h5AAAAAElFTkSuQmCC" nextheight="1093" nextwidth="2048" class="image-node embed"><figcaption htmlattributes="[object Object]" class="">The distributed RPC request systems which dapps should follow</figcaption></figure><p>In Web2, load balancers can reliably handle massive traffic. In Web3, there’s an extra layer of <strong>blockchain RPC bottlenecks</strong>, so you need distributed RPC strategies.</p><p>As frontend developers, we don’t design tokenomics, but we <em>do</em> need to ensure that when a user presses “Claim,” it <strong>works reliably—even during a traffic surge</strong>.</p><p>I learned these lessons through painful trial and error. Hopefully, with this checklist, you won’t end up launching another “broken” DApp.</p><p>Thanks for reading. If you enjoyed this, stay tuned for the next post:<br><strong>“Why Do We Keep Seeing 429 Errors? (A Crash Course on RPC Communication)”</strong> — where we’ll dive deeper into RPC.</p>]]></content:encoded>
            <author>bcryptojake@newsletter.paragraph.com (CryptoJake.eth)</author>
            <category>nomoretrash</category>
            <category>bcryptojake</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/70647e88c997552dee409aba1d5e7bcb9c8c8894b4ebf64577dcc02ca42274b8.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Building Auto-trading in Bithumb with chatGPT]]></title>
            <link>https://paragraph.com/@bcryptojake/building-auto-trading-in-bithumb-with-chatgpt</link>
            <guid>0Np4AGDgE7UKN1e9IAVr</guid>
            <pubDate>Thu, 16 Jan 2025 08:34:17 GMT</pubDate>
            <description><![CDATA[Why Do We Need Auto-Trading?The crypto industry recently experienced an incredible bull market. Bitcoin has been holding steady at $100k for several days, and with policies from the Trump administration, the outlook for the coming months seems even more optimistic. So, did I make money during this boom? Unfortunately, no. Despite having plenty of time on my hands, I found it overwhelming to keep up with the flood of information from Twitter and Telegram. While I attempted to consolidate infor...]]></description>
            <content:encoded><![CDATA[<div class="relative header-and-anchor"><h1 id="h-why-do-we-need-auto-trading">Why Do We Need Auto-Trading?</h1></div><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/0b46993bbfa027cdfebc993d67a7dcdb.png" blurdataurl="data:image/png;base64,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" nextheight="782" nextwidth="784" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>The crypto industry recently experienced an incredible bull market. Bitcoin has been holding steady at $100k for several days, and with policies from the Trump administration, the outlook for the coming months seems even more optimistic.</p><p>So, did I make money during this boom? Unfortunately, no. Despite having plenty of time on my hands, I found it overwhelming to keep up with the flood of information from Twitter and Telegram. While I attempted to consolidate information and pick a few promising assets, I lacked the confidence to hold them long-term without solid justification.</p><p></p><div class="relative header-and-anchor"><h2 id="h-why-not-use-cex-auto-trade-features">Why Not Use CEX Auto-Trade Features?</h2></div><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/ee9ec7b41461d30656553a9c6d070b99.png" blurdataurl="data:image/png;base64,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" nextheight="432" nextwidth="577" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Most centralized exchanges (CEXs) provide auto-trading functionalities. However, in my personal experience, these features seem designed to split pooled USDT or KRW into small chunks, maximizing fees for the platform. While the AI behind these features is undoubtedly more mathematically adept than I am and does yield profits, it’s disheartening to see a 50% profit potential reduced to 40% or even 30%.</p><p>This is why I propose an auto-trading system based on various technical indicators, with decision-making delegated to ChatGPT. Such a system could strike a balance between optimizing profits and providing a transparent rationale for its actions.</p><div class="relative header-and-anchor"><h3 id="h-about-the-technical-indicators">About the Technical Indicators</h3></div><p>The indicators I’ve selected for this project include:</p><ul><li><p><strong>Latest Google-Scraped News</strong>: While Twitter scraping is too costly, news headlines can still provide valuable sentiment insights.</p></li><li><p><strong>Fear and Greed Index</strong>: A classic gauge of market sentiment.</p></li><li><p><strong>OHLCV Indicators</strong>: Metrics like moving averages (MA), relative strength index (RSI), stochastic oscillator, MACD, and Bollinger Bands.</p></li><li><p><strong>Candlestick Data</strong>: 30 days of daily candlesticks and 24 hours of minute-level candlesticks.</p></li></ul><p>These indicators form the backbone of the trading logic, and I plan to create scripts that feed this data into GPT for decision-making. While there are likely better indicators out there, these provide a solid starting point.</p><div class="relative header-and-anchor"><h2 id="h-architecture">Architecture</h2></div><hr><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/ffe2c7c803d7db9f50e1a60e8d95f673.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAVCAIAAACor3u9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAC6ElEQVR4nLWVT0gUURzH37VL0CWDQCKErGMQRXnIDu1F8uAeEqWcS0SUEDG3uTS3OY4Kw16cgzGdnlEOgT3s8lzMHxn2zOSl5HNNGAhhTj5XVidmfu1WrpqafS4Dy3u/7+/P9/eWJFXiOG5tbTUMw/M813WTI4LgRyklhCBVcrkcABylAABEVeI4VkpJKQ8RbqNcTpKkvK6TZOsPAcZYHMeVzYrOKK9rIcQ/5L21vQIU+P2IlFJrvZ9gmNNEsThYKAwWChPF8Y5r58dev9yoVLReI1JKpRRjbGF+4cqtFkLI6UtnCSEXz5xYS+v9lctufJqZ+baycrm5Ged34dRJQsi542StXC6Oc8I5lxlT76ceP31ytb2l80E3IeRRd/vH2c9KLUZ7opQCAKXUcqn0dmysv6+vv8+903Fz/otcWiqlxmGMQYaUknMuhJibnfu+uvp1cdHzvDAM2Z6EGQDwbnLSdd27PT23Ozvv3X9IKWWMSSkJnnAcJwieDz0bGn0z2tbWdqP1+nKp9Gpk5K9j0FpLKQHA930hBOecUvpieBgAREYqoJTCQWEpcRxHUSSECMNwPwIAwDlvbGw0DINzbhhGU1OTYRiu66YVMMY450EGHg3DkFKKuUTZWuwGzoBn+L5PM8IwFEIEQRCGYSpAKRVCUErNDNu2TdP0fV9K6TiOEAI9hnlwznEHESEEAGBEKaVt27lcLp/Pd3V1Ya4AkLpICIH3pz9MYwVYTa1FSqmaZ+pbVJNxXdc0TcuyHMdBd6QzqG1s7SFqaGjAm5xzFMBG4Qx3FAiCIJ/P9/b2GoZhWZZpmjjhtAIA0FpHUeQ4DjmWCgz0D1Q2KxgxyYiiqNaQ+jdKKYU+dBzHsiw7w/M8SmkqoKrEcYxewlbgL0lVwPM8nFD9S463dvPYz7eoypbWurJZ2XZOSokNRR8nB2GbwM5AdT/qh3wEAiLbODTbQaPvS8D3fdu2Lcvyff+/COCzgX95hxD4AT+wrrNFtimWAAAAAElFTkSuQmCC" nextheight="1945" nextwidth="2994" class="image-node embed"><figcaption htmlattributes="[object Object]" class="">KRW &lt;&gt; SOL Auto-trading Architecture</figcaption></figure><div class="relative header-and-anchor"><h4 id="h-why-bithumb">Why Bithumb?</h4></div><p>For this sample application, I decided to use Bithumb’s open API, as it is a prominent exchange in South Korea. While I considered decentralized exchanges (DEXs), the high gas fees on recent blockchain networks discouraged me from pursuing that route. Moreover, for automated trading and selling scripts, the APIs provided by CEXs are much more straightforward to work with.</p><p>The primary <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://apidocs.bithumb.com/reference/%EB%A7%88%EC%BC%93%EC%BD%94%EB%93%9C-%EC%A1%B0%ED%9A%8C">Bithumb API endpoints</a> I plan to use are:</p><ul><li><p><strong>[GET] /v1/accounts</strong>: To retrieve account information.</p></li><li><p><strong>[GET] /v1/ticker</strong>: For real-time price data.</p></li><li><p><strong>[POST] /v1/orders</strong>: To place buy or sell orders.</p></li><li><p><strong>[GET] /v1/orderbook</strong>: To access market depth information.</p></li></ul><p>One thing to note: the popular CEX interface npm package <code>ccxt</code> has an outdated implementation for Bithumb. Since it is incompatible with the Bithumb API 2.0, I had to create a custom interface for connecting to the exchange.</p><div class="relative header-and-anchor"><h4 id="h-prerequisites">Prerequisites</h4></div><p>Before using OpenAI’s <code>chat.completion</code> feature, you need to set up billing. OpenAI’s API requires you to create a project, generate an API key, and make a minimum payment of $10 to activate API usage.</p><p>Additionally, for news scraping, I used the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://serpapi.com/"><strong>SERP API</strong></a>, which provides 100 free API calls during the first month after registration. This makes it the most cost-effective choice for scraping Google News headlines.</p><ul><li><p><strong>Stack</strong>: Node.js with TypeScript.</p></li><li><p><strong>Infrastructure</strong>: AWS EC2 for background running, with PM2 for process management.</p></li></ul><p></p><div class="relative header-and-anchor"><h2 id="h-preview">Preview</h2></div><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e65b5a09b5557d2faad808c02c4be0b7.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAgCAIAAAB7KQSlAAAACXBIWXMAAAsTAAALEwEAmpwYAAACoElEQVR4nJ2UsW/aQBTG/TdkDFIGk0gk6mBQ1WKU4SxlMFIHoy5nsmA6GTGdN5vNZGM1GdPRdHRGq9ul46nbpVOj63hmqi7rVfgokJZA2k8M6Mzvvve997DGGEMIxXGcJAkhRD4j7bcghBrGWNd1CKFpmtPptZRS/KVNxnEcTUqZJAkAAGMsd4qQr1JK27YXTJqmntffzXDOZ7NPQoglE4bh4eFhmqaqsOcw9WhRG+e8KObqqCjmuz+cc0rpwmc8vjIMI88/Sykfn9fd3RfOOWNsmcf3/R2NXtUmhFj6xHGs6/rePOrpguH/l+fm5qNhGI/7hDFm7McyD8bY933G2EvyrHug63qWZf+Qh5UO6o6XSFPzUftXFPPdkYQQDw/fNXV9Xmp3YU9q45yvjvg+LXs9mUwcx9F1PYpGm23Y+mXJTKfXCCHHcZIk2bvaC0aUv8jznFK6OQchBCGEUkoIwRgTQhhja0YIoWlarVaL43jlJqXMsts8z7PsNk3T2ewTpXTNcM7jOEYogBAahjGZTLZWuN5rUTIAANu2e70eQijP883oaixbmGazWavVkiRRk96xb0uGMQYh9H0fQnhwcOC6XTWH+/tvjDFK6Wq5NnvwM4pGg8FwPL5CKEAo8Lx+GIa+7yMU9Hq98fhq1cx1bZZ1UakctVrnpmkeH5+YpvnmbdOyLgCwqtWTavVE/fOfMKi8z/P6ntfvdN73+x8QCqJoNBwO1WEUjbb46Hq11TqvVhcmjcbrer1RqRwBYAFgnZ29yrKMc75mimKOUOA4nTLVwHW7YRi67qXrdgeDAUKB63az7PZPH8fpKIdW67xeb5ilALBsu316elavNwCw1Mt5zUAIbbvdbr+z7bZttyGErtt13UvP6ytDhAK1b4SQX6Ng56yAqTfNAAAAAElFTkSuQmCC" nextheight="965" nextwidth="505" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>The key part of this system is getting mathematical indicatiors from orderbook. I attatch my code snippet for everyone.</p><div class="relative header-and-anchor"><h4 id="h-technical-indicators">Technical Indicators</h4></div><p>Here’s a brief explanation of the key indicators used:</p><ul><li><p><strong>SMA 10 (Simple Moving Average)</strong>: A simple average of the closing prices over the last 10 periods. It helps smooth out price action.</p></li><li><p><strong>EMA 10 (Exponential Moving Average)</strong>: Similar to SMA but gives more weight to recent prices, making it more responsive to new information.</p></li><li><p><strong>RSI 14 (Relative Strength Index)</strong>: A momentum oscillator that measures the speed and change of price movements over 14 periods. Values above 70 indicate overbought conditions, while values below 30 suggest oversold conditions.</p></li><li><p><strong>Stochastic K and D</strong>: These indicators measure the closing price relative to a price range over a set period.</p><ul><li><p><strong>Stochastic K (%K)</strong>: Reflects the current closing price as a percentage of the high-low range over a specific period.</p></li><li><p><strong>Stochastic D (%D)</strong>: A smoothed version of %K, usually a 3-period moving average of %K.</p></li></ul></li><li><p><strong>MACD (Moving Average Convergence Divergence)</strong>: A trend-following momentum indicator that shows the relationship between two moving averages of a security’s price (typically 12-day EMA and 26-day EMA).</p></li><li><p><strong>Bollinger Bands</strong>: A volatility indicator consisting of three lines:</p><ul><li><p><strong>Middle Band</strong>: The 20-period simple moving average (SMA) of closing prices.</p></li><li><p><strong>Upper Band</strong>: Two standard deviations above the middle band.</p></li><li><p><strong>Lower Band</strong>: Two standard deviations below the middle band.</p></li></ul></li></ul><pre data-type="codeBlock" text="import {
  BollingerBands,
  EMA,
  MACD,
  RSI,
  SMA,
  Stochastic,
} from &quot;technicalindicators&quot;;

function addTradingIndicator(data: CEX.OHLCV[]) {
	const closePrices = data.map((row) => row.close);
    const highPrices = data.map((row) => row.high);
    const lowPrices = data.map((row) => row.low);

    // Moving Averages
    const SMA_10 = SMA.calculate({ period: 10, values: closePrices });
    const EMA_10 = EMA.calculate({ period: 10, values: closePrices });

    // RSI
    const RSI_14 = RSI.calculate({ period: 14, values: closePrices });

    // Stochastic Oscillator
    const stochastic = Stochastic.calculate({
      high: highPrices,
      low: lowPrices,
      close: closePrices,
      period: 14,
      signalPeriod: 3,
    });

    // MACD
    const macd = MACD.calculate({
      values: closePrices,
      fastPeriod: 12,
      slowPeriod: 26,
      signalPeriod: 9,
      SimpleMAOscillator: false,
      SimpleMASignal: false,
    });

    // Bollinger Bands
    const bollingerBands = BollingerBands.calculate({
      period: 20,
      values: closePrices,
      stdDev: 2,
    });

    return data.map((row, i) => ({
      ...row,
      SMA_10: SMA_10[i] || null,
      EMA_10: EMA_10[i] || null,
      RSI_14: RSI_14[i] || null,
      Stochastic_K: stochastic[i]?.k || null,
      Stochastic_D: stochastic[i]?.d || null,
      MACD: macd[i]?.MACD || null,
      Signal_Line: macd[i]?.signal || null,
      MACD_Histogram: macd[i]?.histogram || null,
      Middle_Band: bollingerBands[i]?.middle || null,
      Upper_Band: bollingerBands[i]?.upper || null,
      Lower_Band: bollingerBands[i]?.lower || null,
    }));
}"><code><span class="hljs-keyword">import</span> {
  <span class="hljs-title">BollingerBands</span>,
  <span class="hljs-title">EMA</span>,
  <span class="hljs-title">MACD</span>,
  <span class="hljs-title">RSI</span>,
  <span class="hljs-title">SMA</span>,
  <span class="hljs-title">Stochastic</span>,
} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"technicalindicators"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addTradingIndicator</span>(<span class="hljs-params">data: CEX.OHLCV[]</span>) </span>{
	const closePrices <span class="hljs-operator">=</span> data.map((row) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> row.close);
    const highPrices <span class="hljs-operator">=</span> data.map((row) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> row.high);
    const lowPrices <span class="hljs-operator">=</span> data.map((row) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> row.low);

    <span class="hljs-comment">// Moving Averages</span>
    const SMA_10 <span class="hljs-operator">=</span> SMA.calculate({ period: <span class="hljs-number">10</span>, values: closePrices });
    const EMA_10 <span class="hljs-operator">=</span> EMA.calculate({ period: <span class="hljs-number">10</span>, values: closePrices });

    <span class="hljs-comment">// RSI</span>
    const RSI_14 <span class="hljs-operator">=</span> RSI.calculate({ period: <span class="hljs-number">14</span>, values: closePrices });

    <span class="hljs-comment">// Stochastic Oscillator</span>
    const stochastic <span class="hljs-operator">=</span> Stochastic.calculate({
      high: highPrices,
      low: lowPrices,
      close: closePrices,
      period: <span class="hljs-number">14</span>,
      signalPeriod: <span class="hljs-number">3</span>,
    });

    <span class="hljs-comment">// MACD</span>
    const macd <span class="hljs-operator">=</span> MACD.calculate({
      values: closePrices,
      fastPeriod: <span class="hljs-number">12</span>,
      slowPeriod: <span class="hljs-number">26</span>,
      signalPeriod: <span class="hljs-number">9</span>,
      SimpleMAOscillator: <span class="hljs-literal">false</span>,
      SimpleMASignal: <span class="hljs-literal">false</span>,
    });

    <span class="hljs-comment">// Bollinger Bands</span>
    const bollingerBands <span class="hljs-operator">=</span> BollingerBands.calculate({
      period: <span class="hljs-number">20</span>,
      values: closePrices,
      stdDev: <span class="hljs-number">2</span>,
    });

    <span class="hljs-keyword">return</span> data.map((row, i) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> ({
      ...row,
      SMA_10: SMA_10[i] <span class="hljs-operator">|</span><span class="hljs-operator">|</span> null,
      EMA_10: EMA_10[i] <span class="hljs-operator">|</span><span class="hljs-operator">|</span> null,
      RSI_14: RSI_14[i] <span class="hljs-operator">|</span><span class="hljs-operator">|</span> null,
      Stochastic_K: stochastic[i]?.k <span class="hljs-operator">|</span><span class="hljs-operator">|</span> null,
      Stochastic_D: stochastic[i]?.d <span class="hljs-operator">|</span><span class="hljs-operator">|</span> null,
      MACD: macd[i]?.MACD <span class="hljs-operator">|</span><span class="hljs-operator">|</span> null,
      Signal_Line: macd[i]?.signal <span class="hljs-operator">|</span><span class="hljs-operator">|</span> null,
      MACD_Histogram: macd[i]?.histogram <span class="hljs-operator">|</span><span class="hljs-operator">|</span> null,
      Middle_Band: bollingerBands[i]?.middle <span class="hljs-operator">|</span><span class="hljs-operator">|</span> null,
      Upper_Band: bollingerBands[i]?.upper <span class="hljs-operator">|</span><span class="hljs-operator">|</span> null,
      Lower_Band: bollingerBands[i]?.lower <span class="hljs-operator">|</span><span class="hljs-operator">|</span> null,
    }));
}</code></pre><p></p><p>After implenmenting calculations for OHLCV-based metrics and upper indicators, we need to analyze with Open AI's API. I constructed prompts that provide GPT with structured data and request trading recommendations. </p><p></p><pre data-type="codeBlock" text="askToGPT({
    recentNews,
    technicalAnalysis,
    lastDecisions,
    fnbIndex,
    myAccountStatus,
  }: {
    recentNews: string;
    technicalAnalysis: string;
    lastDecisions: string;
    fnbIndex: string;
    myAccountStatus: string;
  }) {
    const instructions = this.getInstructions();

    if (!instructions) {
      console.log(&quot;No instruction found&quot;);
      return;
    }

    try {
      const client = new OpenAI({
        apiKey: process.env.OPENAI_API_KEY,
      });
      const response = await client.chat.completions.create({
        model: &quot;gpt-4o&quot;,
        messages: [
          { role: &quot;system&quot;, content: instructions },
          { role: &quot;user&quot;, content: recentNews },
          { role: &quot;user&quot;, content: technicalAnalysis },
          { role: &quot;user&quot;, content: lastDecisions },
          { role: &quot;user&quot;, content: fnbIndex },
          { role: &quot;user&quot;, content: myAccountStatus },
        ],
        response_format: { type: &quot;json_object&quot; },
      });
      return response.choices[0].message.content;
    } catch (error) {
		//...
    }
  }"><code><span class="hljs-title function_ invoke__">askToGPT</span>({
    recentNews,
    technicalAnalysis,
    lastDecisions,
    fnbIndex,
    myAccountStatus,
  }: {
    <span class="hljs-attr">recentNews</span>: <span class="hljs-keyword">string</span>;
    <span class="hljs-attr">technicalAnalysis</span>: <span class="hljs-keyword">string</span>;
    <span class="hljs-attr">lastDecisions</span>: <span class="hljs-keyword">string</span>;
    <span class="hljs-attr">fnbIndex</span>: <span class="hljs-keyword">string</span>;
    <span class="hljs-attr">myAccountStatus</span>: <span class="hljs-keyword">string</span>;
  }) {
    <span class="hljs-keyword">const</span> <span class="hljs-variable constant_">instructions</span> = this.<span class="hljs-title function_ invoke__">getInstructions</span>();

    <span class="hljs-keyword">if</span> (!instructions) {
      console.<span class="hljs-title function_ invoke__">log</span>(<span class="hljs-string">"No instruction found"</span>);
      <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> <span class="hljs-variable constant_">client</span> = <span class="hljs-keyword">new</span> <span class="hljs-title function_ invoke__">OpenAI</span>({
        <span class="hljs-attr">apiKey</span>: process.env.OPENAI_API_KEY,
      });
      <span class="hljs-keyword">const</span> <span class="hljs-variable constant_">response</span> = await client.chat.completions.<span class="hljs-title function_ invoke__">create</span>({
        <span class="hljs-attr">model</span>: <span class="hljs-string">"gpt-4o"</span>,
        <span class="hljs-attr">messages</span>: [
          { <span class="hljs-attr">role</span>: <span class="hljs-string">"system"</span>, <span class="hljs-attr">content</span>: instructions },
          { <span class="hljs-attr">role</span>: <span class="hljs-string">"user"</span>, <span class="hljs-attr">content</span>: recentNews },
          { <span class="hljs-attr">role</span>: <span class="hljs-string">"user"</span>, <span class="hljs-attr">content</span>: technicalAnalysis },
          { <span class="hljs-attr">role</span>: <span class="hljs-string">"user"</span>, <span class="hljs-attr">content</span>: lastDecisions },
          { <span class="hljs-attr">role</span>: <span class="hljs-string">"user"</span>, <span class="hljs-attr">content</span>: fnbIndex },
          { <span class="hljs-attr">role</span>: <span class="hljs-string">"user"</span>, <span class="hljs-attr">content</span>: myAccountStatus },
        ],
        <span class="hljs-attr">response_format</span>: { <span class="hljs-attr">type</span>: <span class="hljs-string">"json_object"</span> },
      });
      <span class="hljs-keyword">return</span> response.choices[<span class="hljs-number">0</span>].message.content;
    } <span class="hljs-keyword">catch</span> (error) {
		<span class="hljs-comment">//...</span>
    }
  }</code></pre><p></p><p>Be aware of the response should be always structured json type. In my case, The response scripts are like this.</p><pre data-type="codeBlock" text="{
  &quot;decision&quot;: &quot;hold&quot; | &quot;buy&quot; | &quot;sell&quot;,
  &quot;volume&quot;: number,
  &quot;reason&quot;: string, 
}"><code>{
  <span class="hljs-string">"decision"</span>: <span class="hljs-string">"hold"</span> <span class="hljs-operator">|</span> <span class="hljs-string">"buy"</span> <span class="hljs-operator">|</span> <span class="hljs-string">"sell"</span>,
  <span class="hljs-string">"volume"</span>: number,
  <span class="hljs-string">"reason"</span>: <span class="hljs-keyword">string</span>, 
}</code></pre><p></p><p>With the trading logic finalized, I automated order placements via Bithumb's API.</p><pre data-type="codeBlock" text="async function buy(percentage: number) {
    try {
      const balances = await this.getBalance(&quot;sol&quot;);
      const tradePrice = await this.getCurrentPrice(&quot;SOL&quot;);
      if (!balances.krw.free || balances.krw.free <= 5000) {
        throw new Error(&quot;Not enough KRW&quot;);
      }

      const volume = (
        (balances.krw.free * (percentage / 100)) /
        tradePrice
      ).toFixed(4);
      const body = {
        market: &quot;KRW-SOL&quot;,
        side: &quot;bid&quot;,
        volume: volume,
        price: String(tradePrice),
        ord_type: &quot;limit&quot;,
      };

      const query = querystring.encode(body);
      const hash = crypto.createHash(&quot;SHA512&quot;);
      const queryHash = hash.update(query, &quot;utf-8&quot;).digest(&quot;hex&quot;);
      const payload = {
        access_key: config.bithumb.apiKey,
        nonce: uuidv4(),
        timestamp: Date.now(),
        query_hash: queryHash,
        query_hash_alg: &quot;SHA512&quot;,
      };
      const token = jwt.sign(payload, config.bithumb.secretKey);

      const order = await bithumbClient.post(&quot;/v1/orders&quot;, body, {
        headers: {
          &quot;Content-Type&quot;: &quot;application/json&quot;,
          Authorization: `Bearer ${token}`,
        },
      });

      return order;
    } catch (error) {
      if (isAxiosError(error)) {
        throw { code: error.status, message: error.message };
      }
      throw error;
    }
  }

  async sell(percentage: number) {
    try {
      const balances = await this.getBalance(&quot;sol&quot;);
      const tradePrice = await this.getCurrentPrice(&quot;SOL&quot;);
      if (!balances.target.free) {
        throw new Error(&quot;Not enough SOL&quot;);
      }

      const amount = balances.target.free * (percentage / 100);
      if (tradePrice! * amount < 5000) {
        throw new Error(&quot;Less than minimum order volume&quot;);
      }

      const body = {
        market: &quot;KRW-SOL&quot;,
        side: &quot;ask&quot;,
        volume: String(amount),
        price: String(tradePrice),
        ord_type: &quot;limit&quot;,
      };

      const query = new URLSearchParams(body).toString();
      const hash = crypto.createHash(&quot;SHA512&quot;);
      const queryHash = hash.update(query, &quot;utf-8&quot;).digest(&quot;hex&quot;);
      const payload = {
        access_key: config.bithumb.apiKey,
        nonce: uuidv4(),
        timestamp: Date.now(),
        query_hash: queryHash,
        query_hash_alg: &quot;SHA512&quot;,
      };
      const token = jwt.sign(payload, config.bithumb.secretKey);

      console.log(body);

      const order = await bithumbClient.post(&quot;/v1/orders&quot;, body, {
        headers: {
          &quot;Content-Type&quot;: &quot;application/json&quot;,
          Authorization: `Bearer ${token}`,
        },
      });
      return order;
    } catch (error) {
      if (isAxiosError(error)) {
        throw { code: error.status, message: error.message };
      }
      throw error;
    }
  }"><code>async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">buy</span>(<span class="hljs-params">percentage: number</span>) </span>{
    <span class="hljs-keyword">try</span> {
      const balances <span class="hljs-operator">=</span> await <span class="hljs-built_in">this</span>.getBalance(<span class="hljs-string">"sol"</span>);
      const tradePrice <span class="hljs-operator">=</span> await <span class="hljs-built_in">this</span>.getCurrentPrice(<span class="hljs-string">"SOL"</span>);
      <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>balances.krw.free <span class="hljs-operator">|</span><span class="hljs-operator">|</span> balances.krw.free <span class="hljs-operator">&lt;</span><span class="hljs-operator">=</span> <span class="hljs-number">5000</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 enough KRW"</span>);
      }

      const volume <span class="hljs-operator">=</span> (
        (balances.krw.free <span class="hljs-operator">*</span> (percentage <span class="hljs-operator">/</span> <span class="hljs-number">100</span>)) <span class="hljs-operator">/</span>
        tradePrice
      ).toFixed(<span class="hljs-number">4</span>);
      const body <span class="hljs-operator">=</span> {
        market: <span class="hljs-string">"KRW-SOL"</span>,
        side: <span class="hljs-string">"bid"</span>,
        volume: volume,
        price: String(tradePrice),
        ord_type: <span class="hljs-string">"limit"</span>,
      };

      const query <span class="hljs-operator">=</span> querystring.encode(body);
      const hash <span class="hljs-operator">=</span> crypto.createHash(<span class="hljs-string">"SHA512"</span>);
      const queryHash <span class="hljs-operator">=</span> hash.update(query, <span class="hljs-string">"utf-8"</span>).digest(<span class="hljs-string">"hex"</span>);
      const payload <span class="hljs-operator">=</span> {
        access_key: config.bithumb.apiKey,
        nonce: uuidv4(),
        timestamp: Date.now(),
        query_hash: queryHash,
        query_hash_alg: <span class="hljs-string">"SHA512"</span>,
      };
      const token <span class="hljs-operator">=</span> jwt.sign(payload, config.bithumb.secretKey);

      const order <span class="hljs-operator">=</span> await bithumbClient.post(<span class="hljs-string">"/v1/orders"</span>, body, {
        headers: {
          <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
          Authorization: `Bearer ${token}`,
        },
      });

      <span class="hljs-keyword">return</span> order;
    } <span class="hljs-keyword">catch</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
      <span class="hljs-keyword">if</span> (isAxiosError(<span class="hljs-function"><span class="hljs-keyword">error</span>)) </span>{
        <span class="hljs-keyword">throw</span> { code: <span class="hljs-keyword">error</span>.status, message: <span class="hljs-keyword">error</span>.message };
      }
      <span class="hljs-keyword">throw</span> <span class="hljs-function"><span class="hljs-keyword">error</span></span>;
    }
  }

  async sell(percentage: number) {
    <span class="hljs-keyword">try</span> {
      const balances <span class="hljs-operator">=</span> await <span class="hljs-built_in">this</span>.getBalance(<span class="hljs-string">"sol"</span>);
      const tradePrice <span class="hljs-operator">=</span> await <span class="hljs-built_in">this</span>.getCurrentPrice(<span class="hljs-string">"SOL"</span>);
      <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>balances.target.free) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Not enough SOL"</span>);
      }

      const amount <span class="hljs-operator">=</span> balances.target.free <span class="hljs-operator">*</span> (percentage <span class="hljs-operator">/</span> <span class="hljs-number">100</span>);
      <span class="hljs-keyword">if</span> (tradePrice<span class="hljs-operator">!</span> <span class="hljs-operator">*</span> amount <span class="hljs-operator">&lt;</span> <span class="hljs-number">5000</span>) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Less than minimum order volume"</span>);
      }

      const body <span class="hljs-operator">=</span> {
        market: <span class="hljs-string">"KRW-SOL"</span>,
        side: <span class="hljs-string">"ask"</span>,
        volume: String(amount),
        price: String(tradePrice),
        ord_type: <span class="hljs-string">"limit"</span>,
      };

      const query <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> URLSearchParams(body).toString();
      const hash <span class="hljs-operator">=</span> crypto.createHash(<span class="hljs-string">"SHA512"</span>);
      const queryHash <span class="hljs-operator">=</span> hash.update(query, <span class="hljs-string">"utf-8"</span>).digest(<span class="hljs-string">"hex"</span>);
      const payload <span class="hljs-operator">=</span> {
        access_key: config.bithumb.apiKey,
        nonce: uuidv4(),
        timestamp: Date.now(),
        query_hash: queryHash,
        query_hash_alg: <span class="hljs-string">"SHA512"</span>,
      };
      const token <span class="hljs-operator">=</span> jwt.sign(payload, config.bithumb.secretKey);

      console.log(body);

      const order <span class="hljs-operator">=</span> await bithumbClient.post(<span class="hljs-string">"/v1/orders"</span>, body, {
        headers: {
          <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
          Authorization: `Bearer ${token}`,
        },
      });
      <span class="hljs-keyword">return</span> order;
    } <span class="hljs-keyword">catch</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
      <span class="hljs-keyword">if</span> (isAxiosError(<span class="hljs-function"><span class="hljs-keyword">error</span>)) </span>{
        <span class="hljs-keyword">throw</span> { code: <span class="hljs-keyword">error</span>.status, message: <span class="hljs-keyword">error</span>.message };
      }
      <span class="hljs-keyword">throw</span> <span class="hljs-function"><span class="hljs-keyword">error</span></span>;
    }
  }</code></pre><div class="relative header-and-anchor"><h3 id="h-conclusion">Conclusion</h3></div><p>The auto-trading system demonstrates a promising approach for retail investors overwhelmed by information overload and skeptical of generic CEX auto-trade functionalities. By integrating technical indicators with GPT’s advanced reasoning capabilities, the system strikes a balance between sophistication and simplicity.</p><div class="relative header-and-anchor"><h3 id="h-future-potential">Future Potential</h3></div><ul><li><p><strong>Improved Data Sources</strong>: Incorporating more granular sentiment analysis from platforms like Reddit or Twitter (if cost-effective).</p></li><li><p><strong>Advanced Trading Logic</strong>: Exploring reinforcement learning models that adapt over time.</p></li><li><p><strong>Multi-Exchange Support</strong>: Extending the system to operate across multiple exchanges for arbitrage opportunities.</p></li><li><p><strong>Perptual Futures Trading Support</strong>: Exploring futures trading to achieve more effective profit potential.</p></li></ul><p></p><p>The journey to building a robust auto-trading system is ongoing, but the foundation is set. This project highlights how modern AI tools can empower individuals to navigate complex markets with confidence.</p><p></p>]]></content:encoded>
            <author>bcryptojake@newsletter.paragraph.com (CryptoJake.eth)</author>
            <category>autotrade</category>
            <category>bithumb</category>
            <category>openai</category>
        </item>
        <item>
            <title><![CDATA[Week1 on Alchemy University for Ethereum dev]]></title>
            <link>https://paragraph.com/@bcryptojake/week1-on-alchemy-university-for-ethereum-dev</link>
            <guid>cHTzsJDWOrc29pilJnL2</guid>
            <pubDate>Fri, 06 Sep 2024 06:57:53 GMT</pubDate>
            <description><![CDATA[Recently, I heard about Alchemy University from my teammate. I'm currently in charge of Frontend part in my team. But as a developer work...]]></description>
            <content:encoded><![CDATA[<p>Recently, I heard about <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.alchemy.com/university">Alchemy University</a> from my teammate. I'm currently in charge of Frontend part in my team. But as a developer working in web3 field, writing solidity and deeply understand about the EVM development is very important for my team and future.</p><p>So, that is reason why  I started Ethereum Development Bootcamp.</p><p>Ethereum, a smart contract platform for decentralized applications, relies heavily on encryption and signing mechanisms for its security and integrity. In this blog post, we will summarize the basics of Ethereum’s encryption and signing based on Alchemy's Week 1 lecture.</p><p></p><div class="relative header-and-anchor"><h2 id="h-hash-function">Hash Function</h2></div><p>The output of a hash function is always of vert specific size. For example, in Ethereum, the SHA-256 algorithm generates a 256-bit (32-byte) hash, regardless of whether the input is a short string or a large file.</p><div class="relative header-and-anchor"><h3 id="h-characteristics">Characteristics</h3></div><ul><li><p><strong>Deterministic</strong> : it will always produce the same output. This ensures consistency, allowing systems to verify data by comparing hash values.</p></li><li><p><strong>pseudorandom :</strong> A small change in the input should result in a significantly different hash output. This is known as the avalanche effect.</p></li><li><p><strong>one-way</strong>: difficult to trace the original by comparing only the hash results</p></li><li><p><strong>fast to compute</strong>: Designed to be fast and efficient to compute, even for large amounts of data</p></li><li><p><strong>collision-resistant</strong>: Makes it extremely difficult to find two different inputs that produce the same hash output</p></li></ul><p></p><div class="relative header-and-anchor"><h3 id="h-use-cases">use cases</h3></div><ul><li><p>Commitments (Protocol &amp; smart contract)</p></li><li><p>Proof of Work</p></li></ul><p></p><div class="relative header-and-anchor"><h2 id="h-public-key-cryptography">Public Key Cryptography</h2></div><div class="relative header-and-anchor"><h3 id="h-symmetric-key">Symmetric Key</h3></div><p>A single key for both encryption and decryption. Both the sender and receiver must have access to the same secret key. Follow the example below:</p><ul><li><p><strong>Secret key</strong>: 324</p></li><li><p><strong>Plain text</strong>: Cat</p></li><li><p><strong>Cipher text</strong>: Fcx (C + 3: F, a + 2: C, t + 4: x)</p></li><li><p>Example: AES (Advanced Encryption Standard)</p></li></ul><p></p><div class="relative header-and-anchor"><h2 id="h-asymmetric-key-public-key-cryptography">Asymmetric Key (= Public Key Cryptography)</h2></div><p>Uses two different keys: a public key and a private key. The public key is used for encryption, while the private key is used for decryption. This can be using like below:</p><ul><li><p><strong>Authenticate</strong>: One key signs, the other verifies</p></li><li><p><strong>Encryption</strong>: One key encrypts, the other decrypts</p></li></ul><p></p><p>The key concept of public key cryptography is whenever user signs with own private key, we can check the public key, so can figure out the ownership of this transaction.</p><p>Let me check this with code snippet.</p><p></p><pre data-type="codeBlock"><code><span class="hljs-comment">// How to sign message. using ethereum-cryptography</span>
const secp <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"ethereum-cryptography/secp256k1"</span>);
const { <span class="hljs-built_in">keccak256</span> } <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"ethereum-cryptography/keccak"</span>);
const { utf8ToBytes } <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"ethereum-cryptography/utils"</span>);

const PRIVATE_KEY <span class="hljs-operator">=</span> <span class="hljs-string">"MY-PRIVATE-KEY"</span>;

<span class="hljs-comment">// includes [signature, recoveryBit]</span>
async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">signMessage</span>(<span class="hljs-params"><span class="hljs-built_in">msg</span></span>) </span>{
    const hashedMsg <span class="hljs-operator">=</span> hashMessage(<span class="hljs-built_in">msg</span>);
    const signature <span class="hljs-operator">=</span> await secp.sign(hashedMsg, PRIVATE_KEY, { recovered: <span class="hljs-literal">true</span> });
    <span class="hljs-keyword">return</span> signature
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">hashMessage</span>(<span class="hljs-params">message</span>) </span>{
    const hash <span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(utf8ToBytes(message));
    <span class="hljs-keyword">return</span> hash
}

module.exports <span class="hljs-operator">=</span> signMessage;</code></pre><p></p><p>The result of signMessage includes signature and recoveryBit. We.can use secep.recover() function and get the public key. (In normal case, it would be your crypto address)</p><p></p><p>Bitcoin and Ethereum both have a transformation process to take a public key and turn it into an address. For Bitcoin it includes <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out chakra-link css-vezt15" href="https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses">a checksum and Base58 encoding</a>. Ethereum's address transformation is quite a bit simpler, its address is the last 20 bytes of the hash of the public key.</p><p style="text-align: start">The important thing to recognize here is that the address is differentiated from the public key, but you can always derive the address if you have the public key.</p><p style="text-align: start"></p><p style="text-align: start">And for your information, Ethereum account address is 42 charaters. but you can check the public key has is over 42 chars. So we can usaully slice the public key after the first bit.</p><div class="relative header-and-anchor"><h2 id="h-proof-of-work">Proof of Work</h2></div><hr><p>PoW is another component of Bitcoin that is essential to its success.<br>One of the first use cases of Proof of Work was to prevent spamming. The  idea is you can make each action more difficult. This action can be architected managing the difficulty.</p><p>Let's take a simple example as following:</p><pre data-type="codeBlock"><code>const SHA256 <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">'crypto-js/sha256'</span>);
const TARGET_DIFFICULTY <span class="hljs-operator">=</span> BigInt(<span class="hljs-number">0x0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff</span>);
const MAX_TRANSACTIONS <span class="hljs-operator">=</span> <span class="hljs-number">10</span>;

const mempool <span class="hljs-operator">=</span> [];
const blocks <span class="hljs-operator">=</span> [];

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addTransaction</span>(<span class="hljs-params">transaction</span>) </span>{
    mempool.<span class="hljs-built_in">push</span>(transaction);
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mine</span>(<span class="hljs-params"></span>) </span>{
    const transactions <span class="hljs-operator">=</span> [];
    let count <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;
    let nonce <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;
    let <span class="hljs-built_in">block</span> <span class="hljs-operator">=</span> { id: blocks.<span class="hljs-built_in">length</span>, transactions, nonce };
    let hash <span class="hljs-operator">=</span> SHA256(JSON.stringify(<span class="hljs-built_in">block</span>));
    
    <span class="hljs-keyword">while</span> (mempool.<span class="hljs-built_in">length</span>) {
        <span class="hljs-keyword">if</span> (count <span class="hljs-operator">&gt;</span><span class="hljs-operator">=</span> MAX_TRANSACTIONS) <span class="hljs-keyword">break</span>;
        transactions.<span class="hljs-built_in">push</span>(mempool.shift());
        count<span class="hljs-operator">+</span><span class="hljs-operator">+</span>;
    }

    <span class="hljs-keyword">while</span> (BigInt(`0x${hash}`) <span class="hljs-operator">&gt;</span><span class="hljs-operator">=</span> TARGET_DIFFICULTY) {
        nonce<span class="hljs-operator">+</span><span class="hljs-operator">+</span>;
        <span class="hljs-built_in">block</span> <span class="hljs-operator">=</span> { id: blocks.<span class="hljs-built_in">length</span>, transactions, nonce };
        hash <span class="hljs-operator">=</span> SHA256(JSON.stringify(<span class="hljs-built_in">block</span>));
    }

    blocks.<span class="hljs-built_in">push</span>({ ...block, hash })
}

module.exports <span class="hljs-operator">=</span> {
    TARGET_DIFFICULTY,
    MAX_TRANSACTIONS,
    addTransaction, 
    mine, 
    blocks,
    mempool
};</code></pre><p>What our computer does is increasing nonce value by plus 1. We can set the difficulty value to adjust the difficultiy of mining.</p><div class="relative header-and-anchor"><h2 id="h-recap-first-week-with-qna">Recap First week with QnA.</h2></div><hr><p>Q. How do distributed p2p networks agree on what data is valid without a central administrator running the show?</p><p>A. Consensus mechanisms.</p><p>It means the bitcoin network decides validity of new data based on who is able to produce a valid proof of work</p><p></p><p>Q. How is the block hash calculated?</p><p>A: A hashing function takes data as input, and returns a unique hash.</p><p></p><p>Q. What is "valid" hash?</p><p>A. A valid hash for a blockchain is a hash that meets certain requirements.</p><ol><li><p>Block index is one greater than latest block index</p></li><li><p>Block previous hash equal to latest block hash</p></li><li><p>Block hash meets difficulty requirement</p></li><li><p>Block hash is correctly calculated.</p></li></ol><p></p><p>If you liked this post, please subscribe and share. Thank you for reading, and I'll see you next time!</p><p></p>]]></content:encoded>
            <author>bcryptojake@newsletter.paragraph.com (CryptoJake.eth)</author>
            <category>web3</category>
            <category>ethereum</category>
            <category>jio_jake</category>
            <category>solidity</category>
        </item>
        <item>
            <title><![CDATA[Understanding the Shift to Web3 Frontend dev]]></title>
            <link>https://paragraph.com/@bcryptojake/understanding-the-shift-to-web3-frontend-dev-1</link>
            <guid>jW4t25JeScwtB5tedgOB</guid>
            <pubDate>Thu, 22 Aug 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[I'm @jio_jake, Web3 frontend developer in @Yooldo_Games.I just started my tech blog sharing my techy ...]]></description>
            <content:encoded><![CDATA[<figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e36bc71092f46004524695e81270f9d6.webp" blurdataurl="data:image/png;base64,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" nextheight="1024" nextwidth="1024" class="image-node embed"><figcaption htmlattributes="[object Object]" class="">Image created by <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://openai.com/index/dall-e-3/">Dall-E-3</a></figcaption></figure><p></p><p>Hi there!  I'm <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/jio_jake">@jio_jake</a>, Web3 frontend developer in <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/Yooldo_Games">@Yooldo_Games</a>.</p><p>I just started my tech blog sharing my techy lesson points to all Web3 beginners who feel nervous like my last year. Now I'm the one of the Web3 Ethereum builders but usually I have no idea of market stuff. But what I definitely can tell all crypto dev newbie is building in Web3 frontend is AWESOME and very clear. I want to share my tips from today so keep subscribing please <span data-name="laughing" class="emoji" data-type="emoji">😆</span> </p><div class="relative header-and-anchor"><h2 id="h-what-frontend-usually-do-normally">What Frontend usually do normally?</h2></div><hr><p>First of all, In my personal opinion, there would be no superior or inferior between Web2 FE and Web3 FE. Please just think as one of the field of business.</p><p>Based on my experience, Frontend developer should have skills like below:</p><ul><li><p><strong>UX(User Experience).</strong> Track the activity of users is always the key of frontend.</p></li><li><p><strong>Javascript.</strong> The world is consist of javascript. I swear.</p></li><li><p><strong>React-like framework.</strong> If you want to be hired in company, we have to accustomed to framework.</p></li><li><p><strong>Collaboration &amp; Communication.</strong> Frontend is literally the end part of every tech and visual part. Sometimes, we have to align all team mates as one team.</p></li><li><p><strong>Problem Solving.</strong> This ability is based on how i know about our team business and knowing in-house tech structure</p></li></ul><p>Frontend engineers struggle to enhance user experience. This might involve translating complex business jargon into more user-friendly language or optimizing the user journey to reduce friction. However, in Web3 companies, the role of a frontend engineer takes on a slightly different approach when it comes to user-friendliness.</p><p></p><div class="relative header-and-anchor"><h2 id="h-user-experience-in-web3-dapp-service">User experience in Web3 dapp service</h2></div><hr><p>Very first time, I reviewed the projects in current team. But I can't get in anything. the terms are not explained well. but a lot of users are gathered and chatting in community. I want to know what makes these users gathered. The key point was in <strong>exclusive group culture</strong>.</p><p>Here are the ideal case of Web2 service user journey:</p><ol><li><p><strong>Users discover our services for the first time through advertisements, viral marketing, recommendation from friends or direct searches.</strong></p></li><li><p>The newbie user comes to our service.</p></li><li><p><strong>Our service fully take care about users.</strong> preventing outbound, recommend some contents or items suitable with user's taste.</p></li><li><p>This makes the user loyalty. He(She) becomes a big fan of our service.</p></li></ol><p></p><p>But honestly, in normal Web3 service as following below:</p><blockquote><p>Please note that your service is written on the premise that it has a huge number of fans and builders.</p></blockquote><ol><li><p>The user who in exclusive chat first heard about our service. The service announced a huge partnership and shouted out from KOL(Key Opinion Leader)</p></li><li><p>The newbie user decide to trust our service because of the reputation of the name of big partner.</p></li><li><p>The user spend some ethereum to participate the event. and connect the wallet in our service.</p></li><li><p>Those kind of things happened again, and <strong>WOW! we made the hype of project</strong>.</p></li></ol><p></p><p>The interesting part as Frontend developer is that <strong><em>user voluntarily keep studying to use our services. </em></strong>This is such a amazing moment compared to web2 culture. because there are so many competing services in the web2 market that users can easily go away from even a little bit of barriers.</p><p>So, We don't need to caring about basic crypto wallet related tutorial and using our service. Then what is exactly user want for our service and what points should we have more weight on?</p><div class="relative header-and-anchor"><h2 id="h-my-pov-trade-off-between-squeezing-and-products">My POV: Trade-off between squeezing and products</h2></div><hr><p>Related above question, The answer would be totally different by team condition. In my case, I'm now in the blockchain gaming field. we provide pretty nice games to users. Even if Saying a little bit distance off, there's no place in a blockchain gaming company that's as passionate about game as ours:</p><figure float="none" width="845px" data-type="figure" class="img-center" style="max-width: 845px;"><img src="https://storage.googleapis.com/papyrus_images/2ff13f2cd8f80ee62666965ba27a859e.png" blurdataurl="data:image/png;base64,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" nextheight="1620" nextwidth="2880" class="image-node embed"><figcaption htmlattributes="[object Object]" class="">Troublepunk, the battle royal game in PC</figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/762f7b264ebae2960ec4badf89052e0a.png" blurdataurl="data:image/png;base64,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" nextheight="670" nextwidth="1186" class="image-node embed"><figcaption htmlattributes="[object Object]" class="">RPD, mobile defense game in Android</figcaption></figure><p></p><p>However, we are now living in the era of dophamine, and having a tendency easily feeling tired of long contents. degen and pre-degen(newbie who wanna be the degen) want to spend their time very efficiently. So we have to prepare really quick and easy interaction for most of our (potential) users. So that's why most of projects just focus on daily check-in or selling limited NFT collections. That's super easy. Only linking your wallet and clicking the mint button.</p><p>So, We need to focus on how quickly users can successfully interact with our dapp. Inducing users to only clicking mint button. and business logic should be place on the back side. It is kind of nice way to put complex work behind of service.</p><p></p><div class="relative header-and-anchor"><h2 id="h-lets-example-if-i-was-a-fe-of-paragraph">Let's Example: If I was a FE of paragraph?</h2></div><hr><p>Let's see the example of <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://paragraph.xyz/">paragraph</a></p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/31ad12d9893433b8ea49f3f5ea65772a.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAgCAIAAACZ9R/cAAAACXBIWXMAABYlAAAWJQFJUiTwAAAEWklEQVR4nLWVf2gbZRjHr5MV/c+B4HBO6jbLtKPCNoWJA9tqHa4OUQsOt//Ev2JFChW7X01tm1+Xay53l+TyJrkmaVIWuoV1HahjrQhdStokhtlsR7rbzYM0Gv9ZXXrt5dJXcte1aZdCnfjw8PLcve99eJ7v+xwPYsLt/9GR/x1hoVxmwvnkCJSgz3XrunSYCaf/NcKIUWbC2dmle6fpWOPx1u8NhJmgt4TQY1S5a3Xmw0eO1uzbf65bt7qrU9ZNEXaX3+0LOZmgDfjsLh/wDhkx6kKP0epgABN0MkHABD3+EAm8lRFmgt6xswaperamtv6Vujd27al77eBb9YeP7tj58ptvv7d734Ga2tdrauufe2HP3lcPlieyhtBjVPu3FzrPd2M4eXE4PHzpysXhsGcggOG2kWs/Mr4hwAxaSRrDyY4z2j6U3IgwE3SPAY/Fk1Cxu7OzkUgklUpFIjcTiUQkcnN6eiqTyUAIl4vFRPJWr8n6OMLZY7BMRuOSJOXzC+l0emJigmXZaHQqplg0GhUEQRTFfD4/GY33oQS6AUEBxkLSieQtCGEul+M4LpPJZLNZXrFcLicIghpACOOJJE45KcCsIYwYBdy+jjPa6zd+hhAWZJnnea7MBEFgWZbjOEmSNkU4ANNr6J+MxiCEsiwvF5cLUgGWmSzLq2ssnjQTpcTLC7GVF6Keq2jqVnQ6gSqfrMuCdvu0OtNEJKoeHfOMafa3Z2fn5OWiInA+EAhwHKfuxuJJC0lXQHR29apaQAhPIkfqEGTkO3D/zwx/797o6OjuXS+iJlQUxcoItRAUtyWTv6mImZFxx/tf/P1HSX8IoSiK4XD4L+U6VESFQqjSs20q9usWtaicBWF3zqRSpf5TWrCwKG12I9fHI1qdlXYPbOgL79me/h/GJyGEPM+rXcDzfDqd5jhOXVmWFQQBQnj1p4mzPThwe9cQfSh5aTiEHDh56DSmtFYxm80qHfU7y7KZTEb9PpfLSVIpi5c+0SOHTl++HOpDqRWEHqMYn7/1y/MW5hqE8GF+QRQXl6SC6quxKC7Oz+chhEbn1c81Wsbn12NlWpAOj40Gt+/cfjC/wPGCMJe7MfYL8HgDQ6HAUCh8ZfQOe/e+MMfxwoP5h6nUDA08pMPzeHc61J/9kXJLBWmhIC3I8lJRlhSV193IRi2CQ8G9zRrkqQ8+/QacaKNbNLYWjb21vRR/2OY40UZ//LXjuIZq0dg+aqORXa0IUl977Cvg9ugw25oWzac6kW2NSHUjsr0ReboJqWpAqptK8famUvDMu6U3VQ3Itgbk+Rakurnhsw4aPEKYcBvhcLkYn3dw0B8IMoy3HyesJNmPE/04geFWC0HgJEkD4GEGSgd8fq/PDzxlhagI0lFSiHQyOAV0qBXFbQaM1KFWPUYYMNJooSwUsNpdpJNZ8fVy2i0kbaGcq45TQHWr3WW1u9QAp0D5GQu1MigrTzNl6mycTE84lrfi/wC1AVIL01jNBwAAAABJRU5ErkJggg==" nextheight="1592" nextwidth="1090" class="image-node embed"><figcaption htmlattributes="[object Object]" class="">When I first signup in paragraph. I faced this error. WTH?</figcaption></figure><p></p><p>Have you ever seen this error?<br>This error occurs because of unsupported chain. Basically my MetaMask chain was Linea, so I have to switch my wallet chain to Ethereum Mainnet.</p><p></p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/4afb37add0f17b00cdc21437ae203c71.png" blurdataurl="data:image/png;base64,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" nextheight="1592" nextwidth="1564" class="image-node embed"><figcaption htmlattributes="[object Object]" class="">I have to change the network to Ethereum Mainnet, and thenn.</figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c08363c50f0d984c6d71779688d62475.png" blurdataurl="data:image/png;base64,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" nextheight="1594" nextwidth="1560" class="image-node embed"><figcaption htmlattributes="[object Object]" class="">OK, now I'm get in!</figcaption></figure><p></p><p>The point is I have to clicking top-left side button and select the networks. Most of Web3 users are really expertise in these kind of things happened, but this might be much easily solved.</p><p>It is usually good way to hide some unnecessary process behind action button. That's the key point to save users time spent.</p><blockquote><p>I guess paragraph team used <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/WalletConnect/web3modal?tab=readme-ov-file">web3-modal</a> for wallet connection.<br>All code below sourced from my assumption.</p></blockquote><p></p><pre data-type="codeBlock" language="typescript"><code><span class="hljs-keyword">const</span> <span class="hljs-title function_">SignInButton</span> = (<span class="hljs-params"></span>) =&gt; {
  <span class="hljs-keyword">const</span> { <span class="hljs-attr">open</span>: openWeb3Modal } = <span class="hljs-title function_">useWeb3Modal</span>();
  <span class="hljs-keyword">const</span> { address, isConnecting } = <span class="hljs-title function_">useAccount</span>();
  <span class="hljs-keyword">const</span> chainId = <span class="hljs-title function_">useChainId</span>();
  <span class="hljs-keyword">const</span> { switchChainAsync } = <span class="hljs-title function_">useSwitchChain</span>();

  <span class="hljs-keyword">const</span> <span class="hljs-title function_">handleOnclick</span> = <span class="hljs-keyword">async</span> (<span class="hljs-params"></span>) =&gt; {
    <span class="hljs-comment">// check current user wallet chain is supportive or not</span>
     <span class="hljs-keyword">if</span> (!supportedChains.<span class="hljs-title function_">has</span>(chainId)) {
       <span class="hljs-keyword">await</span> <span class="hljs-title function_">switchChainAsync</span>({ <span class="hljs-attr">chainId</span>: mainnet.<span class="hljs-property">id</span> });
     }
     <span class="hljs-title function_">openWeb3Modal</span>();
  }
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleOnclick}</span>&gt;</span>
     {isConnecting ? 'Awaiting Confirmation' : 'Signin'}
    <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
  )
}</code></pre><p></p><p>Web3-modal is based on <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://walletconnect.com/">Walletconnect</a>, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://viem.sh/">Viem</a> and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://wagmi.sh/">Wagmi</a>. So we can get easily supports from many hooks which can enhance user wallet status. The one last detail is some of users trying to test our service. They tries to change the chain network just before transaction execution. And they reports (usually upload on twitter) their glory found just before. So the environment check is always important. Like below:</p><ul><li><p>Is user address is same as off-chain registered address?</p></li><li><p>Is the chain user trying to connect is our supportive chain? </p></li></ul><p>There are many tips during interacting smart contract. I'll share all of tips to this blog soon<span data-name="smiley" class="emoji" data-type="emoji">😃</span> </p><div class="relative header-and-anchor"><h2 id="h-wrapping-up">Wrapping up</h2></div><hr><p>As I mentioned earlier, I don't think Web3 frontend development is basically different from Web2 frontend development. In fact, the technical aspects of Web3 are often sharper and more defined than some of the more ambiguous blockchain usages in the market. <br>While Web3 services may sometimes feel less user-friendly, there is good opportunity for us to contribute by analyzing our target users, designing user journeys that meet user's needs, and refining the UX to optimize unnecessary friction.</p><p>Today, I shared a simple example related to UX of wallet action, but I'll also touch on some key areas that Web3 frontend developers should consider in near future.</p><p>If you liked this post, please subscribe and share. Thank you for reading, and I'll see you next time!</p><p></p>]]></content:encoded>
            <author>bcryptojake@newsletter.paragraph.com (CryptoJake.eth)</author>
            <category>web3</category>
            <category>frontend</category>
            <category>jio_jake</category>
            <category>yooldo</category>
            <category>blockchain</category>
            <category>linea</category>
            <category>gamefi</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/e33f075da84b8c55d66d5cf777bf3e65.jpg" length="0" type="image/jpg"/>
        </item>
    </channel>
</rss>