<?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>Buidling Onchain</title>
        <link>https://blog.dannwee.xyz</link>
        <description>I write developer guides that focuses on integrating diverse tools to deliver seamless and holistic consumer experiences. Empower yourself to build powerfully onchain today.</description>
        <lastBuildDate>Fri, 16 Oct 2026 03:14:27 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>Buidling Onchain</title>
            <url>https://storage.googleapis.com/papyrus_images/9bd206ad4a5f3dcda3e950427c6b391e.png</url>
            <link>https://blog.dannwee.xyz</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[The Liquidity Problem That DAMM v2 Solved]]></title>
            <link>https://blog.dannwee.xyz/the-liquidity-problem-that-damm-v2-solved</link>
            <guid>Ins8Fd3Mdetufb9ansnP</guid>
            <pubDate>Mon, 23 Mar 2026 16:45:38 GMT</pubDate>
            <description><![CDATA[@latebuild wrote a piece a while back on how fee extraction is structurally murdering memecoin liquidity. The diagnosis is right. But the solution was never “another router” or a middleware patch. It's a program upgrade. Recently, Meteora deployed native fee compounding in DAMM v2. It's a new fee mode baked into the AMM program itself where the compounding happens inside the swap instruction, atomically, on every single trade.What We DeployedDAMM v2 Release 0.2.0 introduces a new pool collect...]]></description>
            <content:encoded><![CDATA[<p><a target="_blank" rel="noopener noreferrer nofollow" class="dont-break-out css-146c3p1 r-bcqeeo r-1ttztb7 r-qvutc0 r-37j5jr r-1inkyih r-rjixqe r-16dba41 r-1ddef8g r-tjvw6i r-1loqt21" href="https://x.com/latebuild/status/2030729259124429009?s=20"><u>@latebuild wrote a piece a while back on how fee extraction is structurally murdering memecoin liquidity</u></a>. The diagnosis is right. But the solution was never “another router” or a middleware patch. <strong>It's a program upgrade.</strong></p><p>Recently, Meteora deployed <strong>native fee compounding</strong> in <strong>DAMM v2</strong>. It's a new fee mode baked into the AMM program itself where the compounding happens inside the swap instruction, atomically, on every single trade.</p><h2 id="h-what-we-deployed" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>What We Deployed</strong></h2><p><em>DAMM v2 Release 0.2.0 introduces a new pool </em><strong><em>collect fee mode</em></strong><em>: </em><strong><em>Compounding</em></strong><em>.</em></p><p>When a pool creator sets this mode, a configurable percentage (between 0% to 100%) of quote token trading fees are compounded directly back into pool reserves on every swap. The rest remains claimable. </p><p>Just <strong>ONE</strong> parameter, <strong>compoundingFeeBps, </strong>to control the split.</p><p>Set it to 10000 bps (100%) and every bps of trading fees earned goes back into the pool. Set it to 5000 bps (50%) and you collect half, compound the other half.</p><p>This new fee mode is baked into the core AMM, deployed onchain, audited, and open-source.</p><h2 id="h-why-program-level-compounding-changes-everything" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Why Program-Level Compounding Changes Everything</strong></h2><p>Every constant-product AMM operates on the same invariant:</p><p><strong>x · y = k where x is base token reserves, y is quote token reserves, and k is the constant that defines the pool's liquidity. </strong></p><p>So in other words, a larger k = deeper pool liquidity = less slippage.</p><p>Previously, DAMM v2 fees are collected and claimed separately due to the quote token fee earnings feature. Many builders loved this as it didn't require the protocol or the liquidity provider to dump the memecoin into SOL or USDC when collecting fees. But this led to a <strong>limitation.</strong> <em>The pool liquidity doesn't grow from trading activity, it only grows when someone manually adds liquidity.</em></p><p><strong>With CollectFeeMode.Compounding, k grows on every trade.</strong></p><p>When a swap executes, the program calculates the trading fee, splits it according to <strong>compoundingFeeBps</strong>, and adds the compounding portion directly back to the quote token reserves, all within the same instruction. <strong>No separate transaction. No external claim. The fee never leaves the pool.</strong></p><figure float="none" data-type="figure" class="img-center"><img src="https://storage.googleapis.com/papyrus_images/9ccb1f4c71fbd9738dda51d4a40e762cb35062c8dedceacf840860bda6b95980.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAXCAIAAADlZ9q2AAAACXBIWXMAAAsTAAALEwEAmpwYAAAFjklEQVR4nJWVW1ATdxTG1yrT0QfbsR1tta3teOlQa60FNVLacqmmWu4V6g2FUlQoKDeNIAG504AhaogMUcNFwCyBpEsICWFjQkk2RHaBQiQugQBJNlGrBcNLqJIO0mGYNjhy5jzs/Pfs+c33nbO7gGOBsNvtDodDIBAAAODu7h4ZGeni4pKYmOjq6rpmzRoSiQQAAJVKnatcKICX3HM4HDabDUVRHNcRBDF7geM6kUiE4ziGYW1tbS9/fEHAsMGAYd0Y1t3fr+3t+UPYLOKCYCNf0CptEwggGIa5IKhCEJPZPFuGYd0ms/lVATabTYWotQO4DtdrB3B5ewcX5EFQc7NI0tLSygV5zSIJCDZKpXBf/4AO18+VOfUKsNun5ucsAEE0hpExhbKTks+g0EojkrP8o5P8fk4IOZkSFnc+ipITSy0ouFaVSiut4PJHRsZ0uF6FqO0zMfV8enou7fYp5woQRDM6aoRapFGUnIzLN6PTC7f5HX7DzXuz7w/rv/Jb6kra4B0Un10Sn83IY5QZx0w6XL+gglaFChRKKxuE5bUN5bUNPKHUZDZ3odiscKilrY4H8SCxUCKDf0cOhkfdAvmyDjUkhl+ci3r77s26pELUZsICCiXMCrCAyWFWcCsbhGK5Egg6kbwjOOIj78DVJPJWv6MhsZTQ6LhansBieSCRwnvI+8nf+bm5k9a9t37JsuUAsOzNVW+vXffBho0f+/kHe/nspRXTzYRVh+sRRHMoJmmVu69b0PGdITMNt3x/OORkCsBgV1OLWcm59BPns09fLLrCqRs2jCCIxkxYxa1SD08vD0+fDzdsBgAXYH68tsLD0+uLnV9mZuWaCat2AFerO8dMpgLmjbQiJiWfkZxLpxazyqt5zmcwa9GMs4r2ZpFEIoXvKJQIovEPCgXr+RjW39nVI5O3i0QtGNb7b+WitqgLxfQGUy07E7wZU8E8lpngFnto9YW4z48FrIw6sOrUwXdD9y7JSfKEas6xS35CUXTIYJzbov+kcwUY1q03mDjMuIaK4+CNiNICcmE6KTl67amDy8kkgEwC9nsC1wrJUO1J9qUDnWrVkMHYhWLOFTgFoFiPdgBXqRRCQY1SLs4rTgfrq5COtjuw6A4slLU1XUyKFDZWi6E6cVODVqubHfIiAJou1GS0PLY+npyYvFLOKqvgTE5MjowS+NDo8Ij50cRTDMMSjoUZLVaj9ZHBZHnZDP5/ND4xPnDvPrOG7XPG79NDJOCzFW/t3eQVt58vgUZHjRiGXUqPyYoP2/4OcOTbrTRKFKswbe5NflWAHjeEUcIB36XA+y+WciMAkIDssoI/Hz6RwXBM4PbUiG+2vA58AgBnj3qkhPtgWC+K9SzCIgzrVSCqfPalCyXZ2wJIqfSsHFZRh1o9ODiM48MQWCPk1SYcCSjOSOZyWCJ+/dDwyCIU2Gw2uaJdq9UZDQSuG9oXHmw0EEYDodXqEERzF+3Bh0YfTk7mUE7XcNgPxie0Oj2K9Uil8KsCHA4HQRASGG7v6GiVS71+3COVy+B2ueauRnd/5j/TVF+nlMMJ4SE5KbEKmfSuRo3jgwRBOG31X8Cz6WcOh4Mvg9JuZCaWntuXFBSWeTzgfOjZstTE0nNqLSrlVd1m5VyMDaNE+OcnhAsq6Nxy2qTtqdPuCwKy2XmeieR1oVtWB24Gdq10Ia/fHe+74xdvvgyqomc21V4+Fbw79OtNUYG76spyrtMoBv3959PTiwD0DfbRqxklt65e4bJmk1ZJv1zDJKwWZSvUwGFwSrJvlRaCbHoj56pSwv97auYbM7/Dy2ZAWC0iWNx3r7+prVmuVKT+mpFRnB1PPRNPTZQrFZpe9Mn4X2PGsd+kTXmltCI241rd9auVrCxG7nWQQylIq4Nuz8f8Ax8QgjH9OSbUAAAAAElFTkSuQmCC" nextheight="718" nextwidth="980" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>This means after every swap:</p><p><strong>x · (y + compoundingFee) = k' where k' &gt; k</strong></p><p>The invariant increases. The pool is structurally deeper than it was before the trade. The next trade has slightly less price impact. The ceiling is slightly higher. Every trade that follows compounds on top of the previous one.</p><p>This is the flywheel <a target="_blank" rel="noopener noreferrer nofollow" class="dont-break-out css-146c3p1 r-bcqeeo r-1ttztb7 r-qvutc0 r-37j5jr r-1inkyih r-rjixqe r-16dba41 r-1ddef8g r-tjvw6i r-1loqt21" href="https://x.com/@latebuild"><u>@latebuild</u></a> described, but enforced at the program level where: <strong>more volume → more fees → higher k → less slippage → bigger buys possible → higher ceiling → more volume</strong></p><figure float="none" data-type="figure" class="img-center"><img src="https://storage.googleapis.com/papyrus_images/36abd54532651ee0fb6d9b48cf41be9ccdf612e18d869435c6e87c6d12000625.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAACXBIWXMAAAsTAAALEwEAmpwYAAAGmElEQVR4nKWVf1BU1xXHj9PS9g+ncdo4RDM6rdoUsGWCSEQTm0iDWsTBVsCII1GJEMEaupuyozP4I20w2bC7sP6gQiKpyL4Ig+ACb4MrCywbF/YhP+IKOwEqm7KG/cH+dt/C3eV2dl98WoUlJmfOvHlz3zvnc+6533sv4NkMIYQxvnTpUlFRkU6nU6lUo6OjfD5frVZTFCUUCvV6PYfD4fF4Wq2WJEmVSlVTUzNrKggBIAgiISEhPDycw+GIxeKwsLCoqKhly5YtWvRMXl7esWPHACA8PDwsLAwAeDweGzg/AGMslTbdULT19g9IiCu9fX31UulNdZeyU6XsVKnUamWnqktDCYSlUmkTxtjj8cyVZ06A0+kcNxisVpvJZGZ83GCwTE5OGE0TRqPd4Rg33GNGUNCeGvBdDM2d9ykAdocrKZ+/PL2AcdiW9+NtueE7OfFZx51O1w8C+GdmMMYDgyMQmw5r0gBWw9LX4Jl1sCAGov8Ca9K+MZp/EIDpwP6sHIhLidhXuDy9IHwn59k0LiTlPpvG/dWOPAlxZd5GzQlgwloViis1tR4PbXe43C632+W2O1x2h8tqtSPka1UorkmloRngQ4h1P/Iz7qVpjPFNtZogiLnimcFqiUQuv4Expqem/DP+xxwh9HAG/pkZxplIrfbOh/yPGAmynx788G1JfuTHGL9fdPqatJFds8BLsMpvZ0AIynLjk/+euKsgcfeh+OSsmC1/25SaE5eUmZLucM8jEoR8GGPdoO75qBUZ4oMZord28jO3v787pWjPHnH2zuI3qxU1cPgPO5IXRq8E+C3AeliyCiAaFiQvjBYfKcQYe2lvaIZ/JlDpe1WnFxfGL+a9BIkL4OAKSP4pHFm9VvinQ//iQsWxouyYLdmxW/dGvLrx56v2/S4hO3Zr7kvb2msDq+ebbysxgBv9imR+xoHyw7vOHow5vGkdd/PrJ1KS+RnnGysAIWSeMDpstqHBociIqPG7Y1ajcdJsebSbfuSf9k6xL6w/2mu7wzFhNCK/78SpU12abqvdZrKYfQgB+wdCSCAUsYLxI/+A8t/dLcWa6yU9raU9rWI1+WF3S/GAqryv43x3S3Fv+9nu68LAS9vHCE2xuhKJxKOjo+z8Aip6IE1vVZXES3sZ5BTtkl/JuUMVd10/WlYYWVm04Vb7SdnlvZcFr338j9jKonii5PUvSO5Qr6ijgWseHw4CApiPBMLhkWG2ew9lihCSyT5nZ+BxWTsa3h2kSgapEtnlvSLu8vKTaypPv3I6d5G44Nci7vN1ZSmDVMkdSkAp/mke/4oFCISi4ZERNi0wivbS027XfZmsxe2676WngyO0nMhvb+Ao6vIVde80VmbWnt1BXtrfIslpkRxqrMzsaOCqmngdDdz2q0c9bqcPIS8duBWKBQKdbigokOnAGrAop9NZLZE8qhCP22k3G1w2o9tmmaLvBwMYDxRFu10el9VuuedxO9keYIxj1r6soXrYfQeVZdf+uD5n9cq01SvTFv8yYenirWsidieuz2mTUxgHNm1omWIcyDI2qKwrSyFKtxClm9M3w5nCuPqKNOnFXYOaWngldv9CeBngOQjYL4LPyJ/BOt474iAAhU7vQ9MYYxUpqr2wgiQ2Hn8LPsiH8yfh2qexjVXRrTX5kP92ccwLb2x48c2fwAaAqB/B2rjIPb9flbZkSYTNPhk6O4O/O3bvOC/rcyJVXpPZ8Mn2q+VJTZ+mthAZzVU7NHIxeDz0xDeT98ZNwaf567GJkeH/2m2u3r4+5igObSaTSSAUGQwG2u1yWCYcFqNz0uKwGB2WCbfNEljkWbvMDLa1tVMUFeK4dzqdn1ys1Ov1c+Efqog9EthDmMlbX19vMgXuxWnvlJemp71TAf3cD8hRr9f/9Ui+Xv/1gwpmZvP5Ln2TyST57LMDF4Vbzp16taQw8r0jcR+8u/XMidTK4t9kp5eKz3z/K5ONbGqVQ9Y24KRCUjQcSIRNL8DmKMhOgsL9Rqs1dH2zANATZnc4NvJ5cHQfcN4ATgYc+jNkb4eCzKVHs61225MhTzGDJ8334Lb87iGPA77UaptJmU731ejof/r7B9RqdU/PrYHbt/v7B3Q63W3tnZ6eW0NDQ19qtf39AzfVXXfHxjQUdVPd1apQKJWdGop6bBKPA5TKzlLxmWoJIZffaCbJC+UVzSTZ2NhUd7W+WkIolZ11V+ubSZlS2dlMyi6UV2goiiRJgVAkEIrOnjt3obzCMvl/2/N/YLjUDfMzYnMAAAAASUVORK5CYII=" nextheight="745" nextwidth="740" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><strong>The key difference is that nothing can break this loop. </strong>There's no bot to go offline. No claim transaction to miss. No swap step that leaks value due to price impact. The compounding is atomic and unconditional.</p><h2 id="h-why-damm-v2-beats-every-alternative" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Why DAMM v2 Beats Every Alternative</strong></h2><p><strong>Most AMMs decide where your fees go. DAMM v2 lets you.</strong></p><p><strong>Raydium AMM v4 </strong>had a fixed 0.25% fee per trade. 0.22% went back to the pool and auto-compounded. 0.03% went to RAY buybacks. That's 88% fee compounded back, but hardcoded. Every pool got the same split regardless of what the token needed. No configuration. No creator fee. No dynamic adjustment.</p><p><strong>PumpSwap</strong> tried to improve on this with a tiered fee schedule based on SOL-denominated market cap. At the lowest tier (0–420 SOL mcap), where the vast majority of graduated tokens live, the split is 0.93% to the protocol, 0.30% to the creator, and 0.02% to the pool. Which leads to a 98.4% non-compounding rate on a 1.25% total fee. At the highest tier (98,240+ SOL), the LP fee rises to 0.20% on a 0.30% total fee. Better, but still lower than the 0.22% Raydium AMM v4 gave. And almost no token reaches that tier. The overwhelming majority of trading volume happens at the bottom of the schedule. No configuration. No creator control.</p><p><strong>DAMM v2</strong> with <strong>CollectFeeMode.Compounding</strong> doesn't iterate on either model. It operates on a fundamentally different one. The pool creator sets compoundingFeeBps at creation (anywhere from 0 to 10,000 basis points) and that percentage of quote token fees compounds directly into pool reserves on every swap. Inside the swap instruction. Same transaction.</p><p>The numbers make the gap obvious. On $100K volume, a DAMM v2 pool with 100% compounding and a 1% fee returns up to $1,000 to the pool, compared to $220 on Raydium and $20 on PumpSwap at the lowest tier. After $1M cumulative volume, the gap accelerates. <strong>Every dollar that flows into k generates future fees that also flow into k. The difference isn't linear. It compounds.</strong></p><figure float="none" data-type="figure" class="img-center"><img src="https://storage.googleapis.com/papyrus_images/499e156ffca0f5f12e8967530e7fe65948879a42d65adde9326f7c84b11e6ceb.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAVCAIAAACor3u9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAGAUlEQVR4nH1UfUxTVxS//mFc3FxcNNMEEplBcRg0skWmyMSvlM1vGFADiFLl0/JVq0XtLF8F2kILrUr7gEcHpRmlooUhWHmrtQgBcTIViLM1TjqUDiiF0NeW9i6lSW064y8nN+f9znvnd8+95zxgMpkIBAKfzw8JCUlwIp5KpUokEgaDwVxEWloahmEajUav1xMIBABASEgIk8mMjIxkMBgYhsGPAlgs1lWrV4fu2uW3zg8AsGzZJxsDAshkclhYGAAgKCjoi5Ur3RoBAZuWOAW2M5nFhw4dXL16FYfDgRDOm+cX7AsL9oV5sxnHcZfvMoDjuM4JrU6nHRkZGRoaGhkZ6e3pGVrEwMCj4ZHR3r5+TW8vdl/drVJbLNaPb9m7AnppFZ1zjcGrzi3kZNCZybRC0gVGVj6LUlTORyUQQqmwXHqdKUPY1cXUAnJ8JjGCX0itrWDcKM0TcfIhhLWtaCY79ypSmF6WvSMu/Mi56AyW8zGLc768vhJEpp4XSG/57CCAJWvX7zkKfAJXBu85dDanStIal3Xlj8FBhEVTykXyOk4CYet6ADYtB8S9gZgCVcpF5fR0m9V2MOt4bFniVzFbwAYAfADwA2D750FJOxO4Z2MuJ4CCihv5lUh+JZJTyMkrE1CKuBl0Zk4BO7ewQlAvhRDKUIEc5baivBJqEuXUYc6l9PzMeKmwTMxjoFXFEEJmTVkqixx6cu/Xx779nkTYeGTruojArdHfkSsppXUcACFEUZTP52dlZXMruBDCudlZHMddJ4gvOkIhUswsyc6ltnd0uEnPF+RyOYLUNP7SVFbKZpdxahD0/R2oHzyQNjXW1dXW1qENjRKNRuOO2R0Ol/NuYuL58PCL0dHJfw02q9VmtULogNCx6DgxNTk1NTllnDK+ef1G+5d24u2Ei3d2UbdKnZhDFzU0GwyGl1rdm7ExrzZ48fQpUlLizvV/jI3rhfIanlTAEpcLW2vEdySCZiGngVfZJBgbHwMbwo+CRczNznp96UoaEbgZACCtrnb2+9ycp5LrfGhVdPDDpyDRH3zjygTAFrAiJQhsA5evXQUptCIn5Ru8WPUHBPJIpGUADKjVbsZLALmJ+sZtCUoN9Y3bAvZ8Cfau+Sxqw+bUUEDwQW6iwGa1NqNctfJXtbKl46bocZ/SSwbH8XqBQC4WSxGEefFin0rlpYTjeM7PlDIBx2nXnWtPf++rv1+N/zPuvGQcx5Vdiv6HqoGHSqyrdWjw4ZMnQyaTybOOOwqFBsPutbf33OuefPfWawfz5vlno8/6BwcGnz1W9d5/NfZ63mx2R4FOp/PfGHCRlncuMyfi4BH/jZtyKRRPAYPBAADIIJOP/xRNjIufNhq9juj58LC//4Z9+w6EhYWHhYULhSKdTvdeYNpolMlkEokERVEMw/R6vWebw8UsJNIZeUtLZ2fXpUtXDAaDVwXTxmkWmy0UinIpFF5lpU6n9czgrCAtLT0iIiI+ISEpiaTTvYIQev7R9Ho9kXji5MnEmFgii83h8/l6vd5T4KVWSySeSEoiFRUXs9hsPl/guQnnJLumBkIHjptx3Gy14FYLbrNarRYcQsfU5OTt27eam5tRFNXptC7S02ZmpqVNTTKZbGjoydzszMyMccG+4I4Ck2nW7nDY7HbX3Nodjg8aXIQnM2+22Ox2m90+b7a4oy7GbXaHA+C4WdUlUbYh6i4x1u5c7ypu3FXc6OluUHXWuqxDLlAqhMo2BGtHsHYR1o4oFUJ1l1jZ5nRUneLfO+u7FCJVp7jvgVzTLVW21Sjbajrkgj8fYcAwMVbOCG9tPN1ST8rP3Rx/GCTHguTYJWlxy3PPrkmOXUpOWHEhxTf+MDgdBShn19KzApKiwPF9gHxq1YUUH1q635kYUFUcXkwLzjmzNjl2aVrccilyQlZ/qhmNv1YaDWZNxnpBplREbhSSRRWnuQVR/BJiJTO2oiBSUBLLK4wsySPwCo6V0A4wafuZtP3cqz8WUXdfyQi5lL6dkR1aRN2dlxZcRN3jJMkhF1O30TN38gqjqlkJCPfUby28/wCUdnPf04FPxAAAAABJRU5ErkJggg==" nextheight="585" nextwidth="901" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><h2 id="h-the-full-damm-v2-stack" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>The Full DAMM v2 Stack</strong></h2><p>Fee compounding alone would be enough to justify the switch. But what makes DAMM v2 a generational upgrade is how compounding stacks with everything else the program already does.</p><figure float="none" data-type="figure" class="img-center"><img src="https://storage.googleapis.com/papyrus_images/0181bc9375bd615aefaa1085c67ceef6446ffc0e12f74cc5ff7e24dac73f1dcb.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAbCAIAAACSpRrNAAAACXBIWXMAAAsTAAALEwEAmpwYAAAIiklEQVR4nJWUe1jSWRrHT5exhtlWLS2vXdS8myVeUPGGgoKoKZHd3FJXC8uKLG+rJiYKInnBlMQ0scbyAmqbRSaaiTiIDJCi4d3UrjM128w26RTzKO1sts/+Mef5Pud5f7/n/f4+5z3n9x6gUqmUSiWBQOBwOHV1dfHx8ReWBoFAsLGxSU5OFolEycnJDAYDhUKx2Wwajab6MwOoVKoXL54XFBQ4OTkWFBRERUWVXCoh55D9/f3hcPdz586x2ezY2NjS0ktYLHbPHhyBQFhYmFepPv6P/j/g7dufh5RjQ8oxsXRganp2UDEikw9NP3n2/MWrsfHpsfHpienZienZAcXIu3e//qnlLwJ+W5gflbaOSFtGpC2P+7kDglrFd/XD4ka54Nt+fpWsiy3rYovbysVt5RL+FXFbeU9rqYR/RdZVvTQvJgyLGxS9dW9eTS998MtSwM+vnz/gnmxvPFKZA22rjRDyiE0s7NVcaG2RT1fLifb66NpCDyEvob3+73Ulfo1l6PoS37oSBKcM01iG5rLQjWXoxczmmAHhNZVKtbAw/+HjMgaY//WdvLtqQFgh71aLJe1kitsLxe3F4vZCEY8ubi9sYUV1cdNFvIvC1lxha66Ily9uL+xvZ3zfWSbtZMq7KySdJc8mv1epVL8tLHxZwfjTie7HfYKhRT0cEqnVPdQnHJYIh/v7RmXSqeGG5jK+iCeZHOoblfeNyh8M9D5UfNc52Nsh73nwSCgY6hOOSHqH+z98/DDzfHZibnGvPnz88AkQx0qA5vq7UYNgFIwbNUgteF6wLhG6/jRUM95eM97eJgOhS4SuJNisJFiDY9Z2JIQ635EcAKNgYBQMPD8YmomcfjlDrynOqshVqVTv599/AuwrjgmtiQq/fiy0MtI0DW5x3tswyfWrkzs2Jjh+c2oXiLMxTHIFUSYrT9iBY5bgqNXGBCfTNPiubH+bTIRNJsKZgjn4LSH8+jFEEe7R9GCPrLdVcG/pMD7tFSA3FmALI8JLY8OKj6AoeBQF75Md6p0dEkg/gKUfQOfvDymKQOSGofP3+VHwKNpebOEhzwvBXjmhXjmhiFwcioLHlUThL8VgaRGTz550S4VfAgR3civojlUXYZcp9lUFjpcptkyydWn2dlrqRmrS+v/M+rmJ2rRUfUqybl6aYfEFcybVjpm3k0WHVlx0rrjoXF3szmb4/fvtK75E0CbiLwO03Yhu5yD5XCyfi21g2tUWWzQw7bgsKJtmSCMCVpYGjQhqiy1YWWup8aAwETBSQN0lay7LqaXKlcuC8m748rlBD2+HclkuMyM9AoWU39exDCDkJQjbgoVte4VtuNvXYByWTVPlLg7Lpq0ecavapfWaW3MltIFp3VwJvVlieqvapalyVwPTksOyvX0N1lS5s7PZX3A3TNiG43PQPzxVdElFXwJu0sgpWHh6mO9ZtPtZ/0Wd8YMd99wV7WQZ42wd42wV42wVYWdyyG7b3+xMD+80UcdqRTtZxThbn/KBpmDhqaHIH5+9FDwSdUsFywCJyAMHjbwit6PUOmzqd9DI46CR595N7vZA0xUYOQN9P2DuCUz8gbUzMECA7RFbfA4aef5hidyOirYM2P0XqORe98CT4QeSbpVK9fqn1zMvni4sLIBExN5TbsFEj9BEBD4RgSfCd+N07YMglkfM4d7AEAm24nTt0avMgiCWQRAL9CozX7A5CGKJXmV2xNwzJWA/0SOU6BGahAyPNPcSt3aOvpyqvVN3nXeznFtV2lD+w5sfQXFCFt7ML9wKvdvUN2grArnJHaHj6qXl4gZx8NJyQei4Ije5e2m5eGs7e2s5eWk5I3RgCB1YgD48QB+OMfII3uqz3zYwwj7okD36xdTc0MxIy4NWGrtgbHZc3c/gSDhpgyZqi16QiUGIiUHIFr0go01YAx2MgQ7GWDdwgyZqFXDThCDWrPbSXudnrBtorBtoYhBiarBbbTE12K12bTYMGVPOTDybYDVdvc67yengvp+fXzwDf/c42214V9vDG9YhvwZwbYjvOg3vdRre2hBfU71gDQADwBoAOwAsALDQ1wow1A6ArPSErPTUhviuAfA1AK62660P6LjbN/dmsdfez79//dNr9XUE9gQkamv7bzYMgfzVVwPiswLAV33ltQLAAXADAL5itec3WsgVqz3XrvNVv1/9tTdYytGA+GhAfNZCfPQ2Bm42DLHYhh8dnBqZVUqGpMv+ouKqpgRSRRq1JiG7Mol8NYFUQSRVxKVciv/HZeJSfDKjPD6dGZ/OVMcJpIpz2VXJlJqUnKtp1Jo0ak1G/vXz9Npsxo03//plYGRArJAsA1S1dEon53oV4yLlZONDSeNDSe19Uc1dQRz1Cj6RFp5SQKCw8In5p+lV7DuCtMt1h9KLqTW3bgnlV251XeMJu+QjIuWkbGqu5q5APjw1NKEQDfYvA5Q3tvOljztkyg6ZksntKKlvJ7E4BbU8xo17KSW1JBYnk8XJq2kt5fIZ9fcZ9feTGbVMbgej/n52VXPRjXu8vsEOmbJHMXb1n92K0ZmhqUGhTLQMUFRejY8l7jt6BoWP8g6NcEKFWcMxZjDUNhekuXuAGczP2MFLzw7+STvg5q7+xg5exg4+1vBAJxQOFhCOxEXiY4knU8m//PxOrOjvkfUuA6Sfz0on5WSSKZlkSjopR63UjAv4/RHBYXv3RURignH4/RGYYFxkDOHo8dPRMXFxpxJI5Dx15vmsRW82NT/2+Em5VC4blas7+b+ApNQMEplGIuctiaZWTn6RNxJtbrUD5oHYbGqBCdljYesAdYFb73R0cHY7m5KRTS34zJVHoRdHHT0u6hVLRqRfXtdXKqvTzmepV6GuY6kUMplKp9KLPp8p+YVkKv2PtKycvGxqvvqRTKWfOHVmfHR8dG5MqpR/DvgdrmWyILMi/wEAAAAASUVORK5CYII=" nextheight="725" nextwidth="855" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><strong>Dynamic fees + Compounding: </strong>Fees adjust based on real-time volatility. Volatile periods don't just earn more, they compound more into the pool, structurally deepening liquidity exactly when it needs to be deepest.</p><p><strong>Fee time / market cap scheduling + Compounding: </strong>Fees start high at launch and decay on your terms, either by time or by market cap. With 100% compounding, the highest-fee period of a token's life builds the deepest liquidity. Set launch fees at 5%, decaying to 0.5% over 24 hours or when a token reaches a certain market cap. The first hour of manic trading builds a liquidity base that other AMMs would take weeks otherwise.</p><p><strong>Position NFTs + Compounding: </strong>Positions are lockable and vestable while still accruing compounded fees. Lock liquidity, set compounding to 100%, and the pool deepens itself without anyone touching it.</p><p><strong>Concentrated liquidity + Low cost: </strong>Price ranges on constant-product without bin management or 0.25 SOL setup costs. Pool + position costs 0.022 SOL. </p><p><strong>Built-in farming: </strong>Most AMMs require a separate staking program to distribute rewards. DAMM v2 doesn't. Farming is native to the pool, with each pool having its own reward vault supporting up to two reward tokens. No external contracts. No extra integration. Creators fund rewards directly into the pool they created.</p><p>Putting it all together, you can launch a token with decaying fees, compound a certain % of fees back, lock the position and let the pool grow from its own volume.</p><h2 id="h-k-should-only-go-up" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>k should only go up</strong></h2><p>The liquidity problem was never about fees existing. It was about fees leaving. Every basis point that exits the pool is a basis point that never compounds into future depth. Over thousands of trades, the difference between a compounding pool and a non-compounding pool isn't incremental. It's categorical.</p><p>CollectFeeMode.Compounding makes the fee flywheel a native AMM primitive. Configurable by the creator. Enforced by the program. Executed inside the swap. No bot. No router. No dependencies.</p><p>The drain doesn't need a plug. It needs to not exist.</p><h2 id="h-get-started-today" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Get Started TODAY</strong></h2><p><strong>DAMM v2 Release 0.2.0 is open source, audited, and live.</strong></p><ul><li><p>CollectFeeMode.Compounding (mode 2) joins the existing BothToken (0) and OnlyB (1) modes.</p></li><li><p>Fee accounting splits tradingFee into claimingFee + compoundingFee.</p></li><li><p>A LiquidityHandler abstraction dispatches math based on fee mode.</p></li></ul><p><strong>Create your first compounding pool with the TypeScript SDK: </strong><a target="_blank" rel="noopener noreferrer nofollow" class="dont-break-out css-146c3p1 r-bcqeeo r-1ttztb7 r-qvutc0 r-37j5jr r-1inkyih r-rjixqe r-16dba41 r-1ddef8g r-tjvw6i r-1loqt21" href="https://npmjs.com/package/@meteora-ag/cp-amm-sdk"><u>npmjs.com/package/@meteora-ag/cp-amm-sdk</u></a></p><p><strong>Documentation: </strong><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.meteora.ag/developer-guide/guides/damm-v2/overview"><u>https://docs.meteora.ag/developer-guide/guides/damm-v2/overview</u></a></p><p><em>Credits to </em><a target="_blank" rel="noopener noreferrer nofollow" class="dont-break-out css-146c3p1 r-bcqeeo r-1ttztb7 r-qvutc0 r-37j5jr r-1inkyih r-rjixqe r-16dba41 r-1ddef8g r-tjvw6i r-1loqt21" href="https://x.com/@sudoku_defi"><em><u>@sudoku_defi</u></em></a><em> for architecting and shipping this upgrade. </em><span data-name="heart" class="emoji" data-type="emoji">❤</span></p>]]></content:encoded>
            <author>dannweeeee@newsletter.paragraph.com (based dann)</author>
            <category>meteora</category>
            <category>damm-v2</category>
            <category>constant-product</category>
            <category>amm</category>
            <category>liquidity</category>
            <category>solana</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/69d87bc27ae16037c361c99f1a51aab523010492231979e436ca7f863e92b492.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Effortlessly Enhance Your dApp with Jupiter Terminal in Just a Few Lines of Code]]></title>
            <link>https://blog.dannwee.xyz/effortlessly-enhance-your-dapp-with-jupiter-terminal-in-just-a-few-lines-of-code</link>
            <guid>X0tUNbg104XpIhRJ83SR</guid>
            <pubDate>Sat, 01 Mar 2025 18:42:12 GMT</pubDate>
            <description><![CDATA[In my previous article, I broke down how Jupiter Swap works and demonstrated how you can 10x your dApp development speed when creating React hooks powered by Jupiter’s APIs for seamless swap integration. But what if I told you that you could 100x your speed and build a dApp with full swap functionality in record time? That is exactly what we are exploring today. Introducing Jupiter Terminal, an open-source, lightweight version of Jupiter that seamlessly embeds into your HTML. Imagine your dAp...]]></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/f21ba682c71711abf9340c870255bad3.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAYCAIAAAAUMWhjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAJBUlEQVR4nF2Re1TSBx/Gf6UJOmebpnjLaZqXtFZZ3hAUb4hcAhUUxUQQU9NUOoaat/KCCGIikfIrUVFJSExEeBl5Y7oMYl3mZdX2lq7V9m6znb1bO+c9e4/v6ew979l5v+f7x/PX83nO8wA2x9gIqjiVBaIKB5H0QW/stRS6NDWni1ZxPQzPj6ZIPZJ6CfSrpDw+mc4lsrgUdifxHBdf3hJKPo8u5ZLYInSpEMkUoc9cxbDEWCYvIZODpzVnZWTl4nFpcdkA4Fd4XW5cX3uwufni66//3nbl1sbGuvmeWW8wlnGkDx9/xagEX7x4/vLlN1tbW/fvP9DMGFbu3fvxh9d88VjP1WGL2bK6urq5+eLf//5ja2vz1atXW1ub29s/qVSqAXF3dRYeeC+QmsVo7haB09N6vd5YVNFlNMxNTWm47UIas8mgn6u6wJ/WzJgWFhoauhoa+NxW7vCgfGlxjlPPZ5XVjY2OaKenDTPahdlZo043o5my3ltZWTIZdDpRe3NzHhGgxB7OR8fkJCHoSYii1LgqAqociziHRVTg44uS4wuSkSW4BGZiVHFSNJuYVJWeXImFlyTDT8fD6ciTTGQoPf5QHjwgK8KXctKPFO6NP+yFD/U4ddiTdNK/IOV4a+EpAGTF3CyLUp6DG+vSZhvTFutTly8RP2vAfdZIWrhImG8kLNbj7nCS7nAwpnqckp3MRAZVphyTFyHGiqPlReFyVpSMeXSYEQ7mH71GOyTJCRFTAiRZQQLigU5SQE9eJJAXuf9MhG9Z3MHKhKCqxKCyuANlsd5lMZ5l0bCCEzDqMXfqURjlqHfOsYOYQ/uzowLSQzwPONolBrllH/OmH/UoPO7DivHhIPY3IL0b4rzr432q4d5VcJ+bDMSdUnhPegAAD45KPYbGRZKI8PQsBJGGIhVjsypJuQXoU+nRqZiP0YlhcYmhyWEeQc67bLozos0XsJRgD1sAcN29C+W/P9nHGQ0DMtx3EVyA1PcB+G4gCQLURh6YoMWrc2MFOSggLQadnUjOScmnofOZWHppOpOell9KZFRnMkrQZHoiPi8BR4an+Do6Be+F3iAjp0+jRsixR5zsnQEg9ZAf9bAPOcwtJ8wlK2gP1W9XfaTnUJxfS6RreZhtLSoQrKQBoyVwbUXsbA367qVTn7eR5hvx+6EOKcFe91vx83WJczUJnzUkCDJCXQAgxNnRVEXR0Qjzhdj8IwdsACDJ31lVcmKyPHq0+OPbcI/ls5jNjnxN9qHBjI9kNE85M3i4KAo4He7LiAgsQEQWoxIZ8ehmbPzpwz6nwzxrUk/QjwcSgwJTggOjXd93AYCDe9/TFqebKMk6WtypI767AODEB0DBUafyeI81v707Id7rHbliQsgFpGsL9qNmtF8nKUBEPQigAt0xYb7ZEeE0ZAo1MbMCjWlJCqLuf6/Yz7Y3xb/P34cTHHrA2ckOeHfZEUHqLHhH7Ef7ILt3AQAqwJ11aN8dJ9sdb4cdLwcwHEbwd4j3tI36EAixA7JP+PMZOICJQ54nomuy0y7mYHlFuejjoTGB/u0leRxUSEPsBwv+dm/dob32kBbb3YI9NpVuezsxgd3xPn0QGxnU5u4H9q/cIG/dod+4Qpb22jbCj5Th4srT4KyUSFbiyfostJTDBG71Ni0MCGZvdI709SwvzgkFfFDSq7mlGJOBV3JOjTvYfLXP7icY5Fd36J8xLd72q857drwd3sCgL10hz/fZGTJwnxoM8+pbkxNKg37mulikAEUg58z12jJRFR2YGxYsjnZZ1FIFeEWn1RimJowzGlDSOyARzQyDN329NI42n+61/Xqf3TvA/97L4bUbZNLRUf++Yx/8RJ/oyvjwgLxfIhEJhW3NMrFQ2X1JIbg0xL0IaJSDX6ytz8xotFrt1NSUrF8yOal++PChxWy+u7KysbY2oVR2BAc8/wvgD0/7l66Q+TTUg1evrVbrhFJlml8YuQFOTqrNZsuTJ0/uW63fvn5tmJ2TCniAeqBrYUqhkvCnZGLtiFQ/ck0/0m+aurk4qVjWjFuMU3N6tTIZ/mdFm67/ZTx1sfvEc58xJlxz6cL0gNgwLNUN9GpBsWEE1Mv6tANX9UP9UwNieUczsKLsfTwpWdcOrmnAVc3Ahnbw2d8G1zXguubGlzrZ6m3ps0/GX8h6tmHQ/6voe7d3wz4d63+mUzxWydbVQ1+qhx4pQasStIxJl2Q9phtCTU8boOaf0wnPabvPGq6UG7tYRmGhQVik6y7RCVg6QdG0gDXNL55sZX7nBtnxcvgRBvnTfdHJ9sGHexb9XGd6qgzdbJ2Qreezp3lVt3ns27xqJa9a0VF9q7NmtJUNqJowxg7cp0Lyiohq7aU+kuQ+lOY/krIe9DOsfYUrVwvMksIV8Ow/YK5/jf+bO/SVG+R5iLOl/+yyqHixm7HAy9NycycuU1SXqaNNGTIOHmSndbESgLkrVLOYaO7OuMtPN3UQlzvSl3jpJh5hiZdpaieZuBkLnZSlZuKGi93vHvY7nvY7Xu9GfusO/c4NsuFip2HETtYTJjkY9QX0ZB3hna4nTNSljbET5ezk61UYoP88Vs49o+CdHb7MVPDOqPhlsqa8sY5iJbd0gndW0ZSt4JYoaummTMKjIN+N0IAvYG6PCUl3UxFmfMJ4ako3gyzh0HoqMsHGMxJOvrSOIb1YKDpP66qg9FTTmsgRgJRbaX20vvXyW8vnj1bX1hdNS+tPnm2/+fnLp1/Nzc3ekve/+efvSpXKsLw8Oyo3a9TTLU2//OuPB2trFrNlYmLi8fqTh1bzVcHlexbrFxsbP26/+e3t2ydPn21uvfxh++eWEjLQXZ0OtlXJBTVDvGqwpVTSxFKJG8fFTTIBB2wuBJsK+gT1KiJ6KQ5uxsQv4ZKMyAgrnTIbFz0fGqqJOKkopYsulbWVEHoaSq+1npe2VssFdWBr1WBn3aiorbGQBIC1OG0rcbw2TVGD1bRnDl0k9FckyWsx09wMNjkGGezrD3Nq/9D+V3foW3foGxh0Gwb93g2yDYP+4v5OG91s+awoYRGik47oYqHaabFMZAAbG3Y5J4pPR7TRUf8BUB4L0XM4B74AAAAASUVORK5CYII=" nextheight="465" nextwidth="620" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>In my <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://dev.dannwee.xyz/develop-world-class-swap-hooks-with-jupiter-exchanges-v6-swap-api">previous article</a>, I broke down how Jupiter Swap works and demonstrated how you can <strong>10x your dApp development speed</strong> when creating React hooks powered by Jupiter’s APIs for seamless swap integration.</p><p><strong><em>But what if I told you that you could 100x your speed and build a dApp with full swap functionality in record time?</em></strong> </p><p>That is exactly what we are exploring today. Introducing <strong>Jupiter Terminal</strong>, an <strong>open-source</strong>, <strong>lightweight version of Jupiter</strong> that seamlessly embeds into your HTML. Imagine your dApp having a swap component that looks and feels just like <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://jup.ag/">jup.ag</a>. That is the added power that Jupiter Terminal brings with <strong>no unnecessary redirects</strong>, <strong>no disruptions</strong>, <strong><em>just a flawless user experience</em></strong>.</p><p>In this Builder Guide, I'll be going through how you can <strong>integrate Jupiter Terminal directly into your HTML, </strong>while also detailing how you can customize the configurations of the swap terminal, allowing you to choose the display mode that best suit your needs. Additionally, I'll also navigate you through some of the JavaScript technical nuances so that you can successfully build from scratch your own swap Terminal.</p><div class="relative header-and-anchor"><h2 id="h-so-what-exactly-is-jupiter-terminal">So, what exactly is Jupiter Terminal?</h2></div><p>Well, think of it as a <strong>lightweight, plug-and-play version</strong> of what you see on <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://jup.ag/">jup.ag</a>. <em>It's core purposes?</em>  Delivering a seamless, end-to-end swap flow that you can integrate effortlessly with only just a few lines of code in your HTML.</p><p>As of today <em>(03/01/2025)</em> in terms of <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://station.jup.ag/docs/">Space Station's documentation</a>, Jupiter's APIs currently only consists of the <strong>Swap V6 API</strong> endpoint (which will be <em>deprecated soon</em>) and also the <strong>newly improved</strong> <strong>Swap V1 API</strong> endpoint that only contains <strong><em>Manual Mode</em></strong>.</p><p>Meanwhile,&nbsp;<strong>Jupiter Terminal is already ahead of the curve</strong>, fully equipped with <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/its0xRay/status/1891418405883801789"><strong><u>Ultra Mode</u></strong></a> and its cutting-edge innovations, such as:</p><p><span data-name="check_mark_button" class="emoji" data-type="emoji"><img src="https://cdn.jsdelivr.net/npm/emoji-datasource-apple/img/apple/64/2705.png" draggable="false" loading="lazy" align="absmiddle"></span> <strong>Real-time slippage estimation (RTSE) </strong>– <em>Leverages real-time swap data from </em><strong><em>actual users</em></strong><em>, </em><strong><em>historical trends</em></strong><em>, and </em><strong><em>heuristics</em></strong><em> to determine the most optimal slippage for each trade, minimizing losses and improving execution efficiency.</em></p><p><span data-name="check_mark_button" class="emoji" data-type="emoji"><img src="https://cdn.jsdelivr.net/npm/emoji-datasource-apple/img/apple/64/2705.png" draggable="false" loading="lazy" align="absmiddle"></span> <strong>Dynamic priority fees </strong>– <em>Estimates optimal transaction fees by </em><strong><em>analyzing</em></strong><em> </em><strong><em>network activity</em></strong><em>, </em><strong><em>swap size</em></strong><em>, and </em><strong><em>interactions with high-traffic accounts</em></strong><em>, ensuring your transactions are prioritized without unnecessary overpayment.</em></p><p><span data-name="check_mark_button" class="emoji" data-type="emoji"><img src="https://cdn.jsdelivr.net/npm/emoji-datasource-apple/img/apple/64/2705.png" draggable="false" loading="lazy" align="absmiddle"></span> <strong>Optimised transaction landing </strong>– <em>Fires transactions across </em><strong><em>multiple providers</em></strong><em> simultaneously, increasing the likelihood of successful execution, especially in volatile market conditions.</em></p><p><span data-name="check_mark_button" class="emoji" data-type="emoji"><img src="https://cdn.jsdelivr.net/npm/emoji-datasource-apple/img/apple/64/2705.png" draggable="false" loading="lazy" align="absmiddle"></span> <strong>Jupiter Shield (coming soon) </strong>– <em>Designed to </em><strong><em>enhance security</em></strong><em> and </em><strong><em>safeguard swaps</em></strong><em> from potential risks in token selection.</em></p><div data-type="twitter" tweetid="1892692770675912938"> 
  <div class="twitter-embed embed">
    <div class="twitter-header">
        <div style="display:flex">
          <a target="_blank" href="https://twitter.com/jup_uplink">
              <img alt="User Avatar" class="twitter-avatar" src="https://storage.googleapis.com/papyrus_images/1cf0d88c994d9b3dab9ea9909a690f09.png">
            </a>
            <div style="margin-left:4px;margin-right:auto;line-height:1.2;">
              <a target="_blank" href="https://twitter.com/jup_uplink" class="twitter-displayname">Jupiter Uplink</a>
              <p><a target="_blank" href="https://twitter.com/jup_uplink" class="twitter-username">@jup_uplink</a></p>
    
            </div>
            <a href="https://twitter.com/jup_uplink/status/1892692770675912938" target="_blank">
              <img alt="Twitter Logo" class="twitter-logo" src="https://paragraph.xyz/editor/twitter/logo.png">
            </a>
          </div>
        </div>
      
    <div class="twitter-body">
      Ultra Mode: the cheaper and better way to swap tokens. 
      <div class="twitter-media">
      <img class="twitter-image" src="https://pbs.twimg.com/ext_tw_video_thumb/1892690751173103616/pu/img/e_2V_vqwgPChKoj-.jpg"> 
    </div>
      
       
    </div>
    
     <div class="twitter-footer">
          <a target="_blank" href="https://twitter.com/jup_uplink/status/1892692770675912938" style="margin-right:16px; display:flex;">
            <img alt="Like Icon" class="twitter-heart" src="https://paragraph.xyz/editor/twitter/heart.png">
            23
          </a>
          <a target="_blank" href="https://twitter.com/jup_uplink/status/1892692770675912938"><p>5:48 • 21 Feb 2025</p></a>
        </div>
    
  </div> 
  </div><div class="relative header-and-anchor"><h2 id="h-step-by-step-guide-integrating-and-configuring-jupiter-terminal-in-your-html">Step-By-Step Guide: Integrating and Configuring Jupiter Terminal in your HTML</h2></div><p>To lay the basis, I will be exploring two approaches of how you can integrate Jupiter Terminal into a Typescript React application, <strong>both of which begin by embedding a &lt;script&gt; tag in a HTML file.</strong> &nbsp;The <u>first method</u> involves <strong>initializing the </strong><code>Jupiter</code><strong> object via </strong><code>window</code> object, while the <u>second method</u> <strong>leverages the @jup-ag/terminal npm package to initialize Jupiter Terminal.</strong></p><div class="relative header-and-anchor"><h3 id="h-declaring-typescript-support-only-for-typescript-react-apps">Declaring Typescript Support (ONLY FOR Typescript React Apps)</h3></div><p><u>As we are building in a Typescript React application for this Builder Guide, we will need to do this step</u>. Since Jupiter Terminal is only importable via CDN, to get proper typing, you will need to create a type declaration file - <code>terminal.d.ts</code> - in your project's <code>types</code> folder and copy the following content.</p><pre data-type="codeBlock" text="declare global {
  interface Window {
    Jupiter: JupiterTerminal;
  }
}"><code><span class="hljs-keyword">declare</span> <span class="hljs-keyword">global</span> {
  <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Window</span> </span>{
    Jupiter: JupiterTerminal;
  }
}</code></pre><div class="relative header-and-anchor"><h2 id="h-embedding-a-lessscriptgreater-tag-in-your-html-file">Embedding a &lt;script&gt; tag in your HTML file</h2></div><p>In today’s development landscape, there are many different React-based frameworks that are widely used for building applications. For this builder guide, we will demonstrate two approaches: one for <strong>a plain React application</strong> and another specifically for <strong>Next.js</strong> projects.</p><div class="relative header-and-anchor"><h3 id="h-react-application-example">React Application Example</h3></div><p>In your <code>index.html</code> file, include the following code to allow Terminal to work anywhere in your web application. </p><pre data-type="codeBlock" text="<head>
	...
	<script src='https://terminal.jup.ag/main-v4.js' data-preload defer></script>
</head>"><code><span class="hljs-operator">&lt;</span>head<span class="hljs-operator">&gt;</span>
	...
	<span class="hljs-operator">&lt;</span>script src<span class="hljs-operator">=</span><span class="hljs-string">'https://terminal.jup.ag/main-v4.js'</span> data<span class="hljs-operator">-</span>preload defer<span class="hljs-operator">&gt;</span><span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">&gt;</span>
<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>head<span class="hljs-operator">&gt;</span></code></pre><div class="relative header-and-anchor"><h3 id="h-nextjs-application-example">Next.js Application Example</h3></div><p>For Next.js applications, you can modify your <code>app.tsx</code> or <code>_app.js</code> file with the Next.js built-in <code>next/script</code> component. </p><pre data-type="codeBlock" text="import Script from 'next/script';

function MyApp({ Component, pageProps }) {
  return (
    <>
      <Script
        src=&quot;https://terminal.jup.ag/main-v4.js&quot;
        strategy=&quot;beforeInteractive&quot; // this allows it to load early like a <head> script
		data-preload // custom attribute
		defer // ensures that it does not block parsing
      />
      <Component {...pageProps} />
    </>
  );
}

export default MyApp;"><code><span class="hljs-keyword">import</span> <span class="hljs-title">Script</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'next/script'</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyApp</span>(<span class="hljs-params">{ Component, pageProps }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span>Script
        src<span class="hljs-operator">=</span><span class="hljs-string">"https://terminal.jup.ag/main-v4.js"</span>
        strategy<span class="hljs-operator">=</span><span class="hljs-string">"beforeInteractive"</span> <span class="hljs-comment">// this allows it to load early like a &lt;head&gt; script</span>
		data<span class="hljs-operator">-</span>preload <span class="hljs-comment">// custom attribute</span>
		defer <span class="hljs-comment">// ensures that it does not block parsing</span>
      <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span>Component {...pageProps} <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
  );
}

export default MyApp;</code></pre><p>If your Next.js application follows the <strong>App Router </strong>structure rather than the older <strong>Pages Router</strong>, you can place the script in the <code>layout.tsx</code> to apply it globally across your application.</p><pre data-type="codeBlock" text="import Script from 'next/script';
import type { Metadata } from 'next';
import './globals.css';

export const metadata: Metadata = {
  title: 'Jupiter Terminal Example',
  description: 'Effortlessly Integrate Jupiter Terminal into your dApp',
};

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang=&quot;en&quot;>
      <head>
        <Script 
          src=&quot;https://terminal.jup.ag/main-v4.js&quot; 
          strategy=&quot;beforeInteractive&quot; 
          data-preload 
          defer 
        />
      </head>
      <body>
        {children}
      </body>
    </html>
  );
}"><code><span class="hljs-keyword">import</span> <span class="hljs-title">Script</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'next/script'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title"><span class="hljs-keyword">type</span></span> { <span class="hljs-title">Metadata</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'next'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'./globals.css'</span>;

export const metadata: Metadata <span class="hljs-operator">=</span> {
  title: <span class="hljs-string">'Jupiter Terminal Example'</span>,
  description: <span class="hljs-string">'Effortlessly Integrate Jupiter Terminal into your dApp'</span>,
};

export default <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">RootLayout</span>(<span class="hljs-params">{ children }: { children: React.ReactNode }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&lt;</span>html lang<span class="hljs-operator">=</span><span class="hljs-string">"en"</span><span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span>head<span class="hljs-operator">&gt;</span>
        <span class="hljs-operator">&lt;</span>Script 
          src<span class="hljs-operator">=</span><span class="hljs-string">"https://terminal.jup.ag/main-v4.js"</span> 
          strategy<span class="hljs-operator">=</span><span class="hljs-string">"beforeInteractive"</span> 
          data<span class="hljs-operator">-</span>preload 
          defer 
        <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>head<span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span>body<span class="hljs-operator">&gt;</span>
        {children}
      <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>body<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>html<span class="hljs-operator">&gt;</span>
  );
}</code></pre><p><em>Now that the Terminal script is embedded in your React application, it’s ready to be used throughout your project. </em><strong><em>But how do we initialize it?</em></strong></p><div class="relative header-and-anchor"><h2 id="h-method-1-initializing-a-jupiter-object-via-window-object">Method 1 - Initializing a Jupiter object via window object</h2></div><div class="relative header-and-anchor"><h3 id="h-react-application-example">React Application Example</h3></div><p>If you are using <strong>a plain React application</strong> that contains <code>index.html</code>, you can simply initialize a Jupiter via the window object in your <code>&lt;body&gt;</code>.</p><pre data-type="codeBlock" text="<body>
  <h1>Jupiter Swap Demo</h1>
  <div id=&quot;jupiter-terminal&quot;></div>
  <script>
    document.addEventListener(&quot;DOMContentLoaded&quot;, function () {
      const jupiterTerminal = document.createElement('div') // create jupiter terminal div
      jupiterTerminal.id = 'jupiter-terminal'
      document.body.appendChild(jupiterTerminal)

      // initialize jupiter terminal
      window.Jupiter.init({
        displayMode: &quot;integrated&quot;,
        integratedTargetId: &quot;integrated-terminal&quot;,
        endpoint: &quot;https://mainnet.helius-rpc.com/?api-key=HELIUS_API_KEY&quot;,
        formProps: {
          fixedOutputMint: false
        }
      });
    });
  </script>
</body>"><code><span class="hljs-operator">&lt;</span>body<span class="hljs-operator">&gt;</span>
  <span class="hljs-operator">&lt;</span>h1<span class="hljs-operator">&gt;</span>Jupiter Swap Demo<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>h1<span class="hljs-operator">&gt;</span>
  <span class="hljs-operator">&lt;</span>div id<span class="hljs-operator">=</span><span class="hljs-string">"jupiter-terminal"</span><span class="hljs-operator">&gt;</span><span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
  <span class="hljs-operator">&lt;</span>script<span class="hljs-operator">&gt;</span>
    document.addEventListener(<span class="hljs-string">"DOMContentLoaded"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      const jupiterTerminal <span class="hljs-operator">=</span> document.createElement(<span class="hljs-string">'div'</span>) <span class="hljs-comment">// create jupiter terminal div</span>
      jupiterTerminal.id <span class="hljs-operator">=</span> <span class="hljs-string">'jupiter-terminal'</span>
      document.body.appendChild(jupiterTerminal)

      <span class="hljs-comment">// initialize jupiter terminal</span>
      window.Jupiter.init({
        displayMode: <span class="hljs-string">"integrated"</span>,
        integratedTargetId: <span class="hljs-string">"integrated-terminal"</span>,
        endpoint: <span class="hljs-string">"https://mainnet.helius-rpc.com/?api-key=HELIUS_API_KEY"</span>,
        formProps: {
          fixedOutputMint: <span class="hljs-literal">false</span>
        }
      });
    });
  <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">&gt;</span>
<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>body<span class="hljs-operator">&gt;</span></code></pre><div class="relative header-and-anchor"><h3 id="h-nextjs-application-example">Next.js Application Example</h3></div><p>For Next.js applications, you can initialize Jupiter Terminal via a <code>useEffect</code> hook and adding a <code>&lt;div&gt;</code> with the <code>id</code> of the terminal that has been declared.</p><pre data-type="codeBlock" text="&quot;use client&quot;;

import React, { useEffect } from &quot;react&quot;;

export default function TerminalExample() {
  useEffect(() => {
    if (typeof window !== &quot;undefined&quot;) {
      window.Jupiter.init({
  		displayMode: &quot;integrated&quot;,
  		integratedTargetId: &quot;integrated-terminal&quot;,
  		endpoint: &quot;https://mainnet.helius-rpc.com/?api-key=HELIUS_API_KEY&quot;,
	  });
    }
  }, []);

  return (
    <div>
      <h1>Jupiter Terminal Example</h1>
      {/* This is where the terminal will be embedded */}
      <div id=&quot;integrated-terminal&quot; style={{ width: &quot;100%&quot;, height: &quot;500px&quot; }}></div>
    </div>
  );
}"><code><span class="hljs-string">"use client"</span>;

<span class="hljs-keyword">import</span> <span class="hljs-title">React</span>, { <span class="hljs-title">useEffect</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react"</span>;

export default <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">TerminalExample</span>(<span class="hljs-params"></span>) </span>{
  useEffect(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    <span class="hljs-keyword">if</span> (typeof window <span class="hljs-operator">!</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"undefined"</span>) {
      window.Jupiter.init({
  		displayMode: <span class="hljs-string">"integrated"</span>,
  		integratedTargetId: <span class="hljs-string">"integrated-terminal"</span>,
  		endpoint: <span class="hljs-string">"https://mainnet.helius-rpc.com/?api-key=HELIUS_API_KEY"</span>,
	  });
    }
  }, []);

  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&lt;</span>div<span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span>h1<span class="hljs-operator">&gt;</span>Jupiter Terminal Example<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>h1<span class="hljs-operator">&gt;</span>
      {<span class="hljs-comment">/* This is where the terminal will be embedded */</span>}
      <span class="hljs-operator">&lt;</span>div id<span class="hljs-operator">=</span><span class="hljs-string">"integrated-terminal"</span> style<span class="hljs-operator">=</span>{{ width: <span class="hljs-string">"100%"</span>, height: <span class="hljs-string">"500px"</span> }}<span class="hljs-operator">&gt;</span><span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
  );
}</code></pre><div class="relative header-and-anchor"><h2 id="h-method-2-initializing-terminal-using-jup-agterminal">Method 2 - Initializing Terminal using @jup-ag/terminal</h2></div><p>Alternatively, you can also initialize Jupiter Terminal via the npm package <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.npmjs.com/package/@jup-ag/terminal">@jup-ag/terminal</a>.</p><div class="relative header-and-anchor"><h3 id="h-prerequisites">Prerequisites</h3></div><ul><li><p>Install <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.npmjs.com/package/@jup-ag/terminal"><strong>@jup-ag/terminal</strong></a> package</p></li></ul><p>After installing this package, you can use a <code>useEffect</code> hook and initialize Terminal like this.</p><pre data-type="codeBlock" text="&quot;use client&quot;;

import React, { useEffect } from &quot;react&quot;;
import &quot;@jup-ag/terminal/css&quot;;

export default function TerminalExample() {
  useEffect(() => {
    if (typeof window !== &quot;undefined&quot;) {
      import(&quot;@jup-ag/terminal&quot;).then((mod) => {
        const { init } = mod;
        init({
          displayMode: &quot;integrated&quot;,
          integratedTargetId: &quot;integrated-terminal&quot;,
          endpoint: &quot;https://api.mainnet-beta.solana.com&quot;,
        });
      });
    }
  }, []);

  return (
    <div>
      <h1>Jupiter Terminal</h1>
      {/* This is where the terminal will be embedded */}
      <div id=&quot;integrated-terminal&quot; style={{ width: &quot;100%&quot;, height: &quot;500px&quot; }}></div>
    </div>
  );
}"><code><span class="hljs-string">"use client"</span>;

<span class="hljs-keyword">import</span> <span class="hljs-title">React</span>, { <span class="hljs-title">useEffect</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@jup-ag/terminal/css"</span>;

export default <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">TerminalExample</span>(<span class="hljs-params"></span>) </span>{
  useEffect(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    <span class="hljs-keyword">if</span> (typeof window <span class="hljs-operator">!</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"undefined"</span>) {
      <span class="hljs-keyword">import</span>(<span class="hljs-string">"@jup-ag/terminal"</span>).<span class="hljs-title">then</span>((<span class="hljs-title">mod</span>) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
        <span class="hljs-title">const</span> { <span class="hljs-title">init</span> } <span class="hljs-operator">=</span> <span class="hljs-title">mod</span>;
        init({
          displayMode: <span class="hljs-string">"integrated"</span>,
          integratedTargetId: <span class="hljs-string">"integrated-terminal"</span>,
          endpoint: <span class="hljs-string">"https://api.mainnet-beta.solana.com"</span>,
        });
      });
    }
  }, []);

  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&lt;</span>div<span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span>h1<span class="hljs-operator">&gt;</span>Jupiter Terminal<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>h1<span class="hljs-operator">&gt;</span>
      {<span class="hljs-comment">/* This is where the terminal will be embedded */</span>}
      <span class="hljs-operator">&lt;</span>div id<span class="hljs-operator">=</span><span class="hljs-string">"integrated-terminal"</span> style<span class="hljs-operator">=</span>{{ width: <span class="hljs-string">"100%"</span>, height: <span class="hljs-string">"500px"</span> }}<span class="hljs-operator">&gt;</span><span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
  );
}</code></pre><div class="relative header-and-anchor"><h2 id="h-jupiter-terminals-display-modes">Jupiter Terminal's Display Modes</h2></div><p>From the code above, I have demonstrated Jupiter Terminal using only the <strong>Integrated</strong> display mode. However, Jupiter Terminal is highly versatile and supports a total of three display modes to suit different use cases: <strong>Integrated, Widget, and Modal</strong>.</p><p>For each display mode, I will outline the customizable parameters that allow you to modify the Terminal’s behavior based on your chosen mode.</p><div class="relative header-and-anchor"><h3 id="h-integrated-terminal">Integrated Terminal</h3></div><pre data-type="codeBlock" text="interface IntegratedTerminal {
  displayMode: &quot;integrated&quot;;
  integratedTargetId: string;

  /** Form configuration options */
  formProps: {
    /** Initial amount to swap */
    initialAmount?: string;
    /** When true, user cannot change the amount (e.g. for Payments) */
    fixedAmount?: boolean;
    /** Initial input token to swap */
    initialInputMint?: string;
    /** When true, user cannot change the input token */
    fixedInputMint?: boolean;
    /** Initial output token to swap */
    initialOutputMint?: string;
    /** When true, user cannot change the output token (e.g. to buy your project's token) */
    fixedOutputMint?: boolean;
  };

  /** Restrict token selection to a predefined list */
  strictTokenList: boolean;

  /** Style using CSS properties */
  containerStyles: React.CSSProperties;

  /** Style using Tailwind CSS class names */
  containerClassName: string;

  /** Triggered when an error occurs during the swap process */
  onSwapError?: ({
    error,
    quoteResponseMeta,
  }: {
    error?: TransactionError;
    quoteResponseMeta: QuoteResponse | null;
  }) => void;

  /** Triggered when a swap transaction is successfully completed */
  onSuccess?: ({
    txid,
    swapResult,
    quoteResponseMeta,
  }: {
    txid: string;
    swapResult: SwapResult;
    quoteResponseMeta: QuoteResponse | null;
  }) => void;
}"><code>interface IntegratedTerminal {
  displayMode: <span class="hljs-string">"integrated"</span>;
  integratedTargetId: string;

  <span class="hljs-comment">/** Form configuration options */</span>
  formProps: {
    <span class="hljs-comment">/** Initial amount to swap */</span>
    initialAmount?: string;
    <span class="hljs-comment">/** When true, user cannot change the amount (e.g. for Payments) */</span>
    fixedAmount?: boolean;
    <span class="hljs-comment">/** Initial input token to swap */</span>
    initialInputMint?: string;
    <span class="hljs-comment">/** When true, user cannot change the input token */</span>
    fixedInputMint?: boolean;
    <span class="hljs-comment">/** Initial output token to swap */</span>
    initialOutputMint?: string;
    <span class="hljs-comment">/** When true, user cannot change the output token (e.g. to buy your project's token) */</span>
    fixedOutputMint?: boolean;
  };

  <span class="hljs-comment">/** Restrict token selection to a predefined list */</span>
  strictTokenList: boolean;

  <span class="hljs-comment">/** Style using CSS properties */</span>
  containerStyles: React.CSSProperties;

  <span class="hljs-comment">/** Style using Tailwind CSS class names */</span>
  containerClassName: string;

  <span class="hljs-comment">/** Triggered when an error occurs during the swap process */</span>
  onSwapError?: ({
    error,
    quoteResponseMeta,
  }: {
    error?: TransactionError;
    quoteResponseMeta: QuoteResponse | null;
  }) =&gt; void;

  <span class="hljs-comment">/** Triggered when a swap transaction is successfully completed */</span>
  onSuccess?: ({
    txid,
    swapResult,
    quoteResponseMeta,
  }: {
    txid: string;
    swapResult: SwapResult;
    quoteResponseMeta: QuoteResponse | null;
  }) =&gt; void;
}</code></pre><div class="relative header-and-anchor"><h3 id="h-widget-terminal">Widget Terminal</h3></div><pre data-type="codeBlock" text="type WidgetPosition = &quot;top-left&quot; | &quot;top-right&quot; | &quot;bottom-left&quot; | &quot;bottom-right&quot;;
type WidgetSize = &quot;sm&quot; | &quot;default&quot;;

interface WidgetTerminal {
  displayMode: &quot;widget&quot;;

  /** Form configuration options */
  formProps: {
    /** Initial amount to swap */
    initialAmount?: string;
    /** When true, user cannot change the amount (e.g. for Payments) */
    fixedAmount?: boolean;
    /** Initial input token to swap */
    initialInputMint?: string;
    /** When true, user cannot change the input token */
    fixedInputMint?: boolean;
    /** Initial output token to swap */
    initialOutputMint?: string;
    /** When true, user cannot change the output token (e.g. to buy your project's token) */
    fixedOutputMint?: boolean;
  };

  /** Restrict token selection to a predefined list */
  strictTokenList: boolean;

  /** When displayMode is 'widget', this is the behaviour and style of the widget */
  widgetStyle?: {
    position?: WidgetPosition;
    size?: WidgetSize;
  };

  /** Triggered when an error occurs during the swap process */
  onSwapError?: ({
    error,
    quoteResponseMeta,
  }: {
    error?: TransactionError;
    quoteResponseMeta: QuoteResponse | null;
  }) => void;

  /** Triggered when a swap transaction is successfully completed */
  onSuccess?: ({
    txid,
    swapResult,
    quoteResponseMeta,
  }: {
    txid: string;
    swapResult: SwapResult;
    quoteResponseMeta: QuoteResponse | null;
  }) => void;
}"><code>type WidgetPosition = "<span class="hljs-attribute">top</span>-<span class="hljs-attribute">left</span>" | "<span class="hljs-attribute">top</span>-<span class="hljs-attribute">right</span>" | "<span class="hljs-attribute">bottom</span>-<span class="hljs-attribute">left</span>" | "<span class="hljs-attribute">bottom</span>-<span class="hljs-attribute">right</span>";
type WidgetSize = "sm" | "default";

interface WidgetTerminal {
  displayMode: <span class="hljs-string">"widget"</span>;

  <span class="hljs-comment">/** Form configuration options */</span>
  formProps: {
    <span class="hljs-comment">/** Initial amount to swap */</span>
    initialAmount?: string;
    <span class="hljs-comment">/** When true, user cannot change the amount (e.g. for Payments) */</span>
    fixedAmount?: boolean;
    <span class="hljs-comment">/** Initial input token to swap */</span>
    initialInputMint?: string;
    <span class="hljs-comment">/** When true, user cannot change the input token */</span>
    fixedInputMint?: boolean;
    <span class="hljs-comment">/** Initial output token to swap */</span>
    initialOutputMint?: string;
    <span class="hljs-comment">/** When true, user cannot change the output token (e.g. to buy your project's token) */</span>
    fixedOutputMint?: boolean;
  };

  <span class="hljs-comment">/** Restrict token selection to a predefined list */</span>
  strictTokenList: boolean;

  <span class="hljs-comment">/** When displayMode is 'widget', this is the behaviour and style of the widget */</span>
  widgetStyle?: {
    <span class="hljs-attribute">position</span>?: WidgetPosition;
    size?: WidgetSize;
  };

  <span class="hljs-comment">/** Triggered when an error occurs during the swap process */</span>
  onSwapError?: ({
    error,
    quoteResponseMeta,
  }: {
    error?: TransactionError;
    quoteResponseMeta: QuoteResponse | null;
  }) =&gt; void;

  <span class="hljs-comment">/** Triggered when a swap transaction is successfully completed */</span>
  onSuccess?: ({
    txid,
    swapResult,
    quoteResponseMeta,
  }: {
    txid: string;
    swapResult: SwapResult;
    quoteResponseMeta: QuoteResponse | null;
  }) =&gt; void;
}</code></pre><div class="relative header-and-anchor"><h3 id="h-modal-terminal">Modal Terminal</h3></div><pre data-type="codeBlock" text="interface ModalTerminal {
  displayMode: &quot;modal&quot;;

  /** Form configuration options */
  formProps: {
    /** Initial amount to swap */
    initialAmount?: string;
    /** When true, user cannot change the amount (e.g. for Payments) */
    fixedAmount?: boolean;
    /** Initial input token to swap */
    initialInputMint?: string;
    /** When true, user cannot change the input token */
    fixedInputMint?: boolean;
    /** Initial output token to swap */
    initialOutputMint?: string;
    /** When true, user cannot change the output token (e.g. to buy your project's token) */
    fixedOutputMint?: boolean;
  };

  /** Restrict token selection to a predefined list */
  strictTokenList: boolean;

  /** Triggered when an error occurs during the swap process */
  onSwapError?: ({
    error,
    quoteResponseMeta,
  }: {
    error?: TransactionError;
    quoteResponseMeta: QuoteResponse | null;
  }) => void;

  /** Triggered when a swap transaction is successfully completed */
  onSuccess?: ({
    txid,
    swapResult,
    quoteResponseMeta,
  }: {
    txid: string;
    swapResult: SwapResult;
    quoteResponseMeta: QuoteResponse | null;
  }) => void;
}"><code>interface ModalTerminal {
  displayMode: <span class="hljs-string">"modal"</span>;

  <span class="hljs-comment">/** Form configuration options */</span>
  formProps: {
    <span class="hljs-comment">/** Initial amount to swap */</span>
    initialAmount?: string;
    <span class="hljs-comment">/** When true, user cannot change the amount (e.g. for Payments) */</span>
    fixedAmount?: boolean;
    <span class="hljs-comment">/** Initial input token to swap */</span>
    initialInputMint?: string;
    <span class="hljs-comment">/** When true, user cannot change the input token */</span>
    fixedInputMint?: boolean;
    <span class="hljs-comment">/** Initial output token to swap */</span>
    initialOutputMint?: string;
    <span class="hljs-comment">/** When true, user cannot change the output token (e.g. to buy your project's token) */</span>
    fixedOutputMint?: boolean;
  };

  <span class="hljs-comment">/** Restrict token selection to a predefined list */</span>
  strictTokenList: boolean;

  <span class="hljs-comment">/** Triggered when an error occurs during the swap process */</span>
  onSwapError?: ({
    error,
    quoteResponseMeta,
  }: {
    error?: TransactionError;
    quoteResponseMeta: QuoteResponse | null;
  }) =&gt; void;

  <span class="hljs-comment">/** Triggered when a swap transaction is successfully completed */</span>
  onSuccess?: ({
    txid,
    swapResult,
    quoteResponseMeta,
  }: {
    txid: string;
    swapResult: SwapResult;
    quoteResponseMeta: QuoteResponse | null;
  }) =&gt; void;
}</code></pre><p><em>For Modal Terminal, you can create a </em><code>&lt;button&gt;</code><em> with an </em><code>onClick</code><em> handler function that calls a function that initializes the Jupiter Terminal.</em></p><pre data-type="codeBlock" text="import React from 'react';

export default function ModalTerminalExample() {
  const handleLaunchTerminal = () => {
    window.Jupiter.init({
  		displayMode: &quot;modal&quot;,
  		integratedTargetId: &quot;modal-terminal&quot;,
  		endpoint: &quot;https://mainnet.helius-rpc.com/?api-key=HELIUS_API_KEY&quot;,
	  });
  };

  return (
    <button onClick={handleLaunchTerminal}>
      <span>Launch Terminal Modal</span>
    </button>
  );

}"><code><span class="hljs-keyword">import</span> <span class="hljs-title">React</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'react'</span>;

export default <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ModalTerminalExample</span>(<span class="hljs-params"></span>) </span>{
  const handleLaunchTerminal <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    window.Jupiter.init({
  		displayMode: <span class="hljs-string">"modal"</span>,
  		integratedTargetId: <span class="hljs-string">"modal-terminal"</span>,
  		endpoint: <span class="hljs-string">"https://mainnet.helius-rpc.com/?api-key=HELIUS_API_KEY"</span>,
	  });
  };

  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&lt;</span>button onClick<span class="hljs-operator">=</span>{handleLaunchTerminal}<span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span>span<span class="hljs-operator">&gt;</span>Launch Terminal Modal<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>span<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>button<span class="hljs-operator">&gt;</span>
  );

}</code></pre><p>Summing this step-by-step guide, we explored two approaches to integrating <strong>Jupiter Terminal</strong> into a <strong>TypeScript React application</strong>. Both methods involved embedding the <strong>Jupiter Terminal script</strong> in the application, with initialization handled either through the <code>window</code><strong> object</strong> or via the <strong>@jup-ag/terminal package</strong> using the <code>useEffect</code> hook.</p><p>For <strong>React applications</strong>, we demonstrated how to include the script in <code>index.html</code> and initialize Jupiter Terminal within the <code>&lt;body&gt;</code>. For <strong>Next.js applications</strong>, we leveraged <strong>Next.js built-in </strong><code>next/script</code><strong> component</strong> and integrated the terminal either in _app.js, layout.tsx, or inside a specific page component using useEffect.</p><p>With Jupiter Terminal now set up in your application, you can take advantage of its <strong>three display modes</strong>—<strong>Integrated, Widget, and Modal</strong>—to best suit your needs.</p><div class="relative header-and-anchor"><h2 id="h-conclusion">Conclusion</h2></div><p>Jupiter Terminal revolutionizes <strong>swap integration</strong> by providing a <strong>lightweight</strong>, <strong>open-source</strong> solution that seamlessly embeds into your dApp with minimal effort.&nbsp;Whether you want a fully embedded experience, a floating widget, or a sleek modal, Jupiter Terminal adapts to your design needs while maintaining <strong>quality end-to-end swaps </strong>without unnecessary redirects or disruptions.</p><p>I hope that this guide has provided you with a <strong>clear and practical understanding</strong> of how to integrate <strong>Jupiter Terminal</strong> into your dApp, enabling you to streamline <strong>swap functionality</strong> with minimal effort. Whether you’re building with <strong>React or any React-based frameworks</strong>, leveraging <strong>window-based initialization</strong> or the <strong>@jup-ag/terminal package</strong>, you now have the tools to create a <u>seamless</u> and <u>efficient</u> swap experience for your users.</p><p>As <strong>Jupiter Terminal continues to evolve</strong>, integrating it into your dApp ensures that you’re always at the forefront of <strong>Solana DeFi innovation</strong>.</p><p>Welcome to the <strong>Jupiverse </strong><span data-name="cat_face" class="emoji" data-type="emoji"><img src="https://cdn.jsdelivr.net/npm/emoji-datasource-apple/img/apple/64/1f431.png" draggable="false" loading="lazy" align="absmiddle"></span></p>]]></content:encoded>
            <author>dannweeeee@newsletter.paragraph.com (based dann)</author>
            <category>solana</category>
            <category>defi</category>
            <category>swap</category>
            <category>jupiter</category>
            <category>terminal</category>
            <category>react</category>
            <category>nextjs</category>
            <category>typescript</category>
            <category>html</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/2430987484e2fa23a7b42077df0753fa.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Programatically Automate Your Smart Contracts with Chainlink Automation]]></title>
            <link>https://blog.dannwee.xyz/programatically-automate-your-smart-contracts-with-chainlink-automation</link>
            <guid>juuNq0h9ayxcwT15Cd15</guid>
            <pubDate>Sat, 15 Feb 2025 16:00:00 GMT</pubDate>
            <description><![CDATA[Smart contracts are powerful tools for automating processes on the blockchain, but they come with a challenge: they rely on external triggers to execute functions. These triggers, typically coming from an Externally Owned Account (EOA) or another contract, can create bottlenecks that are particularly problematic for contracts requiring frequent updates or real-time responsiveness. Now, what if I told you that your smart contracts could maintain themselves, freeing you from the constant need ...]]></description>
            <content:encoded><![CDATA[<p>Smart contracts are powerful tools for automating processes on the blockchain, but they come with a challenge: <strong>they rely on external triggers to execute functions</strong>. These triggers, typically coming from an Externally Owned Account (EOA) or another contract, can create bottlenecks that are particularly problematic for contracts requiring frequent updates or real-time responsiveness.</p><p><strong><em>Now, what if I told you that your smart contracts could <u>maintain</u> themselves, freeing you from the constant need to trigger them manually?</em></strong></p><p>Well, thanks to <strong>Chainlink Automation</strong>, they make automating smart contracts feel like a walk-in-the-park. With Chainlink's Decentralized Oracle Network (Chainlink DON) and onchain Registry architecture, they are able to automate your smart contracts so that you don't have to worry about dealing with setup costs, ongoing maintenance, and risks associated with a centralized automation stack.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/d79870a07e9fec164177381291056898.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAJCAIAAADcu7ldAAAACXBIWXMAAAsTAAALEwEAmpwYAAACpklEQVR4nGWRy2sTQRjAFwVR6El8HaSCbX2UFhRFKlj14lFv9SDVthYKVZRS8dCDKPgv+AcI3qQQEC3WNjVNkyZtGm1i0qTZ3eyyeTXZzc7sI7ObmXVHNilF2h9zmpnv+30PBlkOhFZV0R2Hxn5zHf1jHf1j5wYmr96bvnz75YlrE4d6R3sHn3X3Dx472dPVd+vilbudl24eOX6+b+DB9YnVU49D3SOhzifhC2OhO8+DNyZWTg8Hu0bCPaNhZigw8jbOGCYpl0E2J2g6VmHjV5JfiaS+L675Axv+QHzev74W395M5RPJdCgc/bG4HAiG5+aXQpFYIplJsXApIvyMCgsrbHSzkuFhhoeJLFgMccGouJFS8sUGoxtYM7CJCNCwZTuUUgBAsVg0DB1CKEoSxpi2wBhLUiEc28pxguv+pZQ6DuG5bVHkq9WCaWh73zKZLZbNIoQopV4HdRUVioqmewLiEErprG/u/tDTR6MvWE6glBLHExsmEqTaWkor1VCp5gW7rluW7VIFvn73afrNx/GpD5+/RFpiT++63mF0E6vArqmmpuMmJm3Bw+FJpsWsb84rijgupfmCGk9IqYwYS1ayeUgcYmGyxQFOqvu+rfuDfxaWkxm20iQu0JBu2CayXeoyJiKlMkhviwDae4Lh8alW/qO+rwuWZW8mUoQ4QkENx9gsX42nd5I5YGEMG0hWUU7UdBO3q7ZsR4FWTUUKsGRgUep6O1CBJasNoHkdOK1pvJp5zzBnmMNnl5ZXKaWyLFPqAqNZrOirG5xY0upwdzGG2WzFIkIct0X7fg9GVhvprJRM53mhgvHuM7KscmVHlpX29NsQghVZyXEFVa3vy7KP/yUMsohhNg0T60YTk/3+9ibbbUGosSwrCnmO5zHxJnkw3UH+AZkY07lJ+U9ZAAAAAElFTkSuQmCC" nextheight="299" nextwidth="1080" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>In this Builder Guide, I'll be walking you through the <strong>process of creating automation-compatible contracts and programmatically registering upkeeps</strong>.&nbsp;This will give you full control over the custom logic you want on Chainlink's onchain registry. In return, you’ll receive an <code>upkeepID</code>, which you can use to check balances, fund, modify, or cancel the upkeep all via the registry.</p><p><em>P.S. The </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://automation.chain.link/base-sepolia"><em>Chainlink Automation App</em></a><em> interface also provides pre-built options to register upkeeps with custom triggers, time-based triggers, or log-based triggers if you choose not to programatically register your upkeeps.</em></p><div class="relative header-and-anchor"><h2 id="h-high-level-overview-of-chainlink-automation">High-Level Overview of Chainlink Automation</h2></div><p>The diagram below shows a high-level overview of the flow of how Chainlink Automation works. </p><p>At its core, the <strong>Chainlink Automation Network</strong> relies on the <strong>Automation Registry</strong>, which manages network actors and ensures <strong>Automation Nodes</strong> are compensated for successfully executing upkeeps.</p><p>Automation Nodes operate as a peer-to-peer network using <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.chain.link/architecture-overview/off-chain-reporting">Chainlink’s OCR3 protocol</a>. These nodes rely on the <strong>Automation Registry</strong> to identify which upkeeps to service and independently simulate <code>checkUpkeep</code> functions on their own block nodes to determine when an upkeep is eligible for execution. Once an upkeep is identified, nodes reach consensus through OCR3 and generate a signed report containing the <code>performData</code>, which is then validated on the Registry before execution. This process ensures cryptographic guarantees, enhancing security and reliability. <strong>Additionally, the network is designed with built-in redundancy, ensuring upkeeps are executed even if some nodes go offline.</strong></p><p>Chainlink Automation leverages the same battle-tested transaction manager mechanism used in <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.chain.link/data-feeds">Chainlink Data Feeds</a>. <strong>This ensures a highly reliable automation service capable of executing and confirming transactions even during extreme gas spikes or on chains experiencing significant reorgs.</strong> By combining robust consensus mechanisms with proven transaction management, Chainlink Automation provides a resilient and efficient solution for decentralized task execution.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f9a467523be6f260076bd6f6d1fd3abe.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAQCAIAAAD4YuoOAAAACXBIWXMAAAsTAAALEwEAmpwYAAADdElEQVR4nJ2TW4/bRBTHBwnxyicoPPFp+CYggQqi0CdWFRL7wPNuVaQtaEtWrDZdle0WlVVv6S7pbpqGXBzb8d3jieM4tjMeX+IkziDbbUi3C6o4+mt09LfHP885Z0C1+uLSBx+VSrfnlLpe6HpxoaET9ERNUg1RQqLUX/pvIy9fSTilNAJbWz8DANaurVNKg0myoHRBs4jipNmWK9XuWZ2TFURzf/l0NZnnWj4tnCIJwxBsbNwAAHx2+dvnDal857FhEh35OvIFZVQ5ae3d6x3XUK05MEwiSENeGPDCgOGQINsyHPcUtyfYI4eYti/DcbFRR76Wr0M7oJSC9fUfAABXrn7vBQnDZ9sUhHXTb3bNX/dr27vHu3dbJ3XUN4msjBpN6eGTeqMpKZqjQtzhRxzf15E7tH0VYmiSQhryoUkcL6Z0+hLwzdV1L0h4yeUkt2A8bxpbpcflw79u3e48OTWgSWQDq3DM8khDvoKwDMcqxAw/aLNoaL/86AWAogdfXfnOj6a85Br5G4ZFZM09PlVulh5WqmKDGYqq11NcQRm1GU3W8uIobosdPKuxbUZ70ZJF1UVWcAHg+vUf8x6sJfNUhWNoEmQFhSwnarHDLLHDpWnmOTSJ5URNRi/vPzp6VL9zeNJmjYsBm5ubAIBPPv06ns0FxYEmaXb1p9VWk9GRFewccE9rRt8KVIi1vHurKv5muS59FWIFYduJMkC5fB+A9zdu7C0otZ3Iw5MOI57VWh1GtJ1I0MeF+W/Kpv4N08nNILsHU1C6y4F3Lt/6jaWUzmYppdS2bY7jEMpm3yNJmhYT/38iuwc7+10APt/e7y4Bq+HhyVsC5mnqeWEUJ5Nk/hrgp50W+PDa3r3eOUAQTgXFU/NZjONZHMeULpAVFNOpoexmDewwSSYYY0oXGEeiZI1xFIbJa4CtMgMure0e8m8CKifs7/f/bLTRyIkGZn+MI1ZwWH5w9KD+x4MaK4wUiPN6skEYG6b/7Ezk+P55wPYBC977Mi9RmqycDpmuBjHq+30riONZkkwoTQd2CJGPTIJMApE/yuYkDcOQUjrGIcsjHdqExKuly5v87he/HPzT5KLop43+GAdhSNL0fGMuDElSBUGWJFVR4coJpqDe0cHHN48qKqXzJJnOXgWl1HEcCCEhZGn+R0xn0ySJz5me5/0NNuNPPVvVfTwAAAAASUVORK5CYII=" nextheight="1274" nextwidth="2538" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><div class="relative header-and-anchor"><h2 id="h-step-by-step-guide-create-automation-compatible-contracts-and-register-upkeeps-programmatically">Step-By-Step Guide: Create Automation-Compatible Contracts and Register Upkeeps Programmatically</h2></div><p>To set the stage, this guide will be based on the Base Sepolia network, utilizing <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://soliditylang.org/">Solidity</a> as the programming language and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://book.getfoundry.sh/">Foundry</a> as the contract deployment toolkit. To better illustrate the entire automation flow, I will demonstrate it using a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/dannweeeee/bid-it-dapp">Dutch Auction for Token ICOs</a> project that I previously built. This project is very similar to the concept of <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://phemex.com/academy/what-is-liquidity-bootstrapping-pool-lbp-crypto">Liquidity Bootstrapping Pools</a>.</p><p>Fundamentally, there are 2 main contracts that are used to programatically register automation-compatible contracts:</p><ol><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/dannweeeee/bid-it-contracts/blob/master/src/Auctioneer.sol"><strong>Auctioneer.sol</strong></a><strong> - A Dutch Auction Factory contract for creating and managing Dutch Auctions. This contract contains an internal function to </strong><code>registerUpkeep</code><strong> onto Chainlink.</strong></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/dannweeeee/bid-it-contracts/blob/master/src/DutchAuction.sol"><strong>DutchAuction.sol</strong></a><strong> - A Dutch Auction contract designed for token ICOs. It includes the </strong><code>checkUpkeep</code><strong> and </strong><code>performUpkeep</code><strong> functions, which automate auction finalization, airdrop tokens to users, refund surplus ETH, and burn any remaining tokens.</strong></p></li></ol><div class="relative header-and-anchor"><h3 id="h-prerequisites">Prerequisites</h3></div><ul><li><p><code>forge install</code> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/smartcontractkit/chainlink/tree/fb7d6e88ea3471909a4f9aa29992ec080bba9057">Chainlink's Smart Contract Kit</a>.</p></li><li><p><code>forge install</code> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/OpenZeppelin/openzeppelin-contracts/tree/bcdfa848a6abb37216df861573d9576636e77824">Open Zeppelin Contracts</a>.</p></li><li><p>Get Base Sepolia ETH from <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.alchemy.com/faucets/base-sepolia">Alchemy</a>.</p></li><li><p>Get <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://faucets.chain.link/">LINK</a> Tokens on Base Sepolia Network.</p></li></ul><p><strong>P.S. I will be skipping the core logic of the smart contracts and focus primarily on the Chainlink Automation functions within them. If you’re interested in the full source code, you can find it in this </strong><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/dannweeeee/bid-it-contracts"><strong>Github repository</strong></a><strong>.</strong></p><div class="relative header-and-anchor"><h3 id="h-creating-an-automation-compatible-smart-contract">Creating an <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.chain.link/chainlink-automation/guides/compatible-contracts">Automation-Compatible</a> Smart Contract</h3></div><p>In our context, the<code>DutchAuction.sol</code> will be an automation-compatible smart contract that contains <code>checkUpkeep</code> and <code>performUpkeep</code> functions. These functions allows the Chainlink Automation Network to determine if, when, and how the contract should be automated.</p><ul><li><p><code>checkUpkeep</code> contains the logic that will be executed offchain such that if the criteria in this function that you have set has been met, then the Chainlink Automation Network will automatically trigger <code>performUpkeep</code>. </p></li><li><p><code>performUpkeep</code> contains the logic that will be executed onchain when <code>checkUpkeep</code> returns true. In our case, this function contains the <code>endAuction</code>, <code>airdropToken</code>, <code>refundETH</code> and <code>burnTokens</code> logic that we want to be automatically executed for the Dutch Auction.</p></li></ul><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import {Token} from &quot;./Token.sol&quot;;
import {ReentrancyGuard} from &quot;@openzeppelin/contracts/utils/ReentrancyGuard.sol&quot;;
import {Pausable} from &quot;@openzeppelin/contracts/utils/Pausable.sol&quot;;
import {Ownable} from &quot;@openzeppelin/contracts/access/Ownable.sol&quot;;
import {Math} from &quot;@openzeppelin/contracts/utils/math/Math.sol&quot;;
import {AutomationCompatibleInterface} from &quot;@chainlink/contracts/src/v0.8/automation/AutomationCompatible.sol&quot;;

contract DutchAuction is ReentrancyGuard, Pausable, Ownable, AutomationCompatibleInterface {

	// ... initialise variables, errors, events, mappings and functions

	/////////////////////////////////////////
	///// CHAINLINK AUTOMATION FUNCTIONS ////
	/////////////////////////////////////////

    /**
     * @notice Chainlink Automation: Check if upkeep is needed
     */
    function checkUpkeep(bytes calldata /* checkData */ )
        external
        view
        override
        returns (bool upkeepNeeded, bytes memory /* performData */ )
    {
        upkeepNeeded = (startTime != 0) // auction has started
            &amp;&amp; !auctionEnded // auction hasn't ended yet
            &amp;&amp; (block.timestamp >= endTime || getRemainingTokens() == 0); // time is up OR remaining tokens is 0
        return (upkeepNeeded, &quot;&quot;);
    }

    /**
     * @notice Chainlink Automation: Perform the upkeep
     */
    function performUpkeep(bytes calldata /* performData */ ) external override {
        if (startTime == 0) revert AuctionNotStarted();
        if (auctionEnded) revert AuctionAlreadyEnded();
        if (block.timestamp < endTime &amp;&amp; getRemainingTokens() > 0) revert AuctionNotEnded();

		// Ends the auction
        auctionEnded = true;

        // Tokens to distribute are the total tokens sold
        uint256 tokensToDistribute = getSoldTokens();

        // Get the clearing price
        clearingPrice = getCurrentPrice();

        // Airdrop tokens to bidders proportionally
        for (uint256 i = 0; i < bidders.length; i++) {
            address bidder = bidders[i];
            uint256 ethContribution = ethContributed[bidder];

            // Calculate tokens for this bidder based on their ETH contribution
            uint256 tokenAmount = (ethContribution * tokensToDistribute) / totalEthRaised;
            uint256 ethNeeded = tokenAmount * clearingPrice;
            uint256 refund = ethContribution - ethNeeded;

            if (refund > 0) {
                (bool success,) = bidder.call{value: refund}(&quot;&quot;);
                if (!success) revert RefundFailed();
            }

            if (tokenAmount > 0 &amp;&amp; !hasClaimedTokens[bidder]) {
                hasClaimedTokens[bidder] = true;
                bool success = token.transfer(bidder, tokenAmount * 1e18);
                if (!success) {
                    hasClaimedTokens[bidder] = false;
                    revert TransferFailed();
                }
                emit TokensClaimed(bidder, tokenAmount * 1e18);
            }
        }

        // Burn any remaining tokens
        uint256 remainingTokens = totalTokensForSale - tokensToDistribute;
        if (remainingTokens > 0) {
            token.burn(remainingTokens * 1e18);
        }

        emit AuctionEnded(clearingPrice, tokensToDistribute, totalEthRaised);
    }
}"><code><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.26;</span>

<span class="hljs-keyword">import</span> {<span class="hljs-title">Token</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./Token.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">ReentrancyGuard</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/utils/ReentrancyGuard.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">Pausable</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/utils/Pausable.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">Ownable</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/access/Ownable.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">Math</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/utils/math/Math.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">AutomationCompatibleInterface</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@chainlink/contracts/src/v0.8/automation/AutomationCompatible.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">DutchAuction</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ReentrancyGuard</span>, <span class="hljs-title">Pausable</span>, <span class="hljs-title">Ownable</span>, <span class="hljs-title">AutomationCompatibleInterface</span> </span>{

	<span class="hljs-comment">// ... initialise variables, errors, events, mappings and functions</span>

	<span class="hljs-comment">/////////////////////////////////////////</span>
	<span class="hljs-comment">///// CHAINLINK AUTOMATION FUNCTIONS ////</span>
	<span class="hljs-comment">/////////////////////////////////////////</span>

    <span class="hljs-comment">/**
     * @notice Chainlink Automation: Check if upkeep is needed
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">checkUpkeep</span>(<span class="hljs-params"><span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> <span class="hljs-comment">/* checkData */</span> </span>)
        <span class="hljs-title"><span class="hljs-keyword">external</span></span>
        <span class="hljs-title"><span class="hljs-keyword">view</span></span>
        <span class="hljs-title"><span class="hljs-keyword">override</span></span>
        <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span> upkeepNeeded, <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> <span class="hljs-comment">/* performData */</span> </span>)
    </span>{
        upkeepNeeded <span class="hljs-operator">=</span> (startTime <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>) <span class="hljs-comment">// auction has started</span>
            <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> <span class="hljs-operator">!</span>auctionEnded <span class="hljs-comment">// auction hasn't ended yet</span>
            <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> (<span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span> <span class="hljs-operator">&gt;</span><span class="hljs-operator">=</span> endTime <span class="hljs-operator">|</span><span class="hljs-operator">|</span> getRemainingTokens() <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>); <span class="hljs-comment">// time is up OR remaining tokens is 0</span>
        <span class="hljs-keyword">return</span> (upkeepNeeded, <span class="hljs-string">""</span>);
    }

    <span class="hljs-comment">/**
     * @notice Chainlink Automation: Perform the upkeep
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">performUpkeep</span>(<span class="hljs-params"><span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> <span class="hljs-comment">/* performData */</span> </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> </span>{
        <span class="hljs-keyword">if</span> (startTime <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>) <span class="hljs-keyword">revert</span> AuctionNotStarted();
        <span class="hljs-keyword">if</span> (auctionEnded) <span class="hljs-keyword">revert</span> AuctionAlreadyEnded();
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span> <span class="hljs-operator">&lt;</span> endTime <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> getRemainingTokens() <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span>) <span class="hljs-keyword">revert</span> AuctionNotEnded();

		<span class="hljs-comment">// Ends the auction</span>
        auctionEnded <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;

        <span class="hljs-comment">// Tokens to distribute are the total tokens sold</span>
        <span class="hljs-keyword">uint256</span> tokensToDistribute <span class="hljs-operator">=</span> getSoldTokens();

        <span class="hljs-comment">// Get the clearing price</span>
        clearingPrice <span class="hljs-operator">=</span> getCurrentPrice();

        <span class="hljs-comment">// Airdrop tokens to bidders proportionally</span>
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> bidders.<span class="hljs-built_in">length</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
            <span class="hljs-keyword">address</span> bidder <span class="hljs-operator">=</span> bidders[i];
            <span class="hljs-keyword">uint256</span> ethContribution <span class="hljs-operator">=</span> ethContributed[bidder];

            <span class="hljs-comment">// Calculate tokens for this bidder based on their ETH contribution</span>
            <span class="hljs-keyword">uint256</span> tokenAmount <span class="hljs-operator">=</span> (ethContribution <span class="hljs-operator">*</span> tokensToDistribute) <span class="hljs-operator">/</span> totalEthRaised;
            <span class="hljs-keyword">uint256</span> ethNeeded <span class="hljs-operator">=</span> tokenAmount <span class="hljs-operator">*</span> clearingPrice;
            <span class="hljs-keyword">uint256</span> refund <span class="hljs-operator">=</span> ethContribution <span class="hljs-operator">-</span> ethNeeded;

            <span class="hljs-keyword">if</span> (refund <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span>) {
                (<span class="hljs-keyword">bool</span> success,) <span class="hljs-operator">=</span> bidder.<span class="hljs-built_in">call</span>{<span class="hljs-built_in">value</span>: refund}(<span class="hljs-string">""</span>);
                <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>success) <span class="hljs-keyword">revert</span> RefundFailed();
            }

            <span class="hljs-keyword">if</span> (tokenAmount <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span> <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> <span class="hljs-operator">!</span>hasClaimedTokens[bidder]) {
                hasClaimedTokens[bidder] <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
                <span class="hljs-keyword">bool</span> success <span class="hljs-operator">=</span> token.<span class="hljs-built_in">transfer</span>(bidder, tokenAmount <span class="hljs-operator">*</span> <span class="hljs-number">1e18</span>);
                <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>success) {
                    hasClaimedTokens[bidder] <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
                    <span class="hljs-keyword">revert</span> TransferFailed();
                }
                <span class="hljs-keyword">emit</span> TokensClaimed(bidder, tokenAmount <span class="hljs-operator">*</span> <span class="hljs-number">1e18</span>);
            }
        }

        <span class="hljs-comment">// Burn any remaining tokens</span>
        <span class="hljs-keyword">uint256</span> remainingTokens <span class="hljs-operator">=</span> totalTokensForSale <span class="hljs-operator">-</span> tokensToDistribute;
        <span class="hljs-keyword">if</span> (remainingTokens <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span>) {
            token.burn(remainingTokens <span class="hljs-operator">*</span> <span class="hljs-number">1e18</span>);
        }

        <span class="hljs-keyword">emit</span> AuctionEnded(clearingPrice, tokensToDistribute, totalEthRaised);
    }
}</code></pre><p>** Please note that the logic in <code>checkUpkeep</code> and <code>performUpkeep</code> is completely customisable based on your automation trigger. The only important thing you need to do is to <strong>inherit the </strong><code>AutomationCompatibleInterface</code> from Chainlink.</p><div class="relative header-and-anchor"><h3 id="h-instantiate-a-programatically-upkeep-creation-smart-contract">Instantiate a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.chain.link/chainlink-automation/guides/register-upkeep-in-contract">Programatically Upkeep Creation</a> Smart Contract</h3></div><p>Now that we have created an automation-compatible contract - <code>DutchAuction.sol</code>, we can create <code>Auctioneer.sol</code>, which includes the <code>registerUpkeep</code> function. This function is triggered whenever a user deploys <code>DutchAuction.sol</code>, programmatically registering it onto the Chainlink Automation Network.</p><p>The <code>Auctioneer.sol</code> covers the following main areas:</p><ul><li><p>Import <code>LinkTokenInterface</code> to instantiate an ERC20 token interface for transferring of LINK.</p></li><li><p>Create a <code>RegistrationParams</code> struct for when calling <code>registerUpkeep</code> function on the Chainlink Registrar contract.</p></li><li><p>Instantiate an interface called <code>AutomationRegistrarInterface</code> that contains the <code>registerUpkeep</code> function.</p></li><li><p>Create an internal function that approve the transfer of LINK tokens, initialises the registration parameters and call <code>registerUpkeep</code>.</p></li></ul><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import {DutchAuction} from &quot;./DutchAuction.sol&quot;;
import {IERC20Metadata as IERC20} from &quot;@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol&quot;;
import {LinkTokenInterface} from &quot;@chainlink/contracts/src/v0.8/shared/interfaces/LinkTokenInterface.sol&quot;;

// Create a RegistrationParams struct
struct RegistrationParams {
    address upkeepContract;
    uint96 amount;
    address adminAddress;
    uint32 gasLimit;
    uint8 triggerType;
    IERC20 billingToken;
    string name;
    bytes encryptedEmail;
    bytes checkData;
    bytes triggerConfig;
    bytes offchainConfig;
}

// Instantiate an Automation Registrar Interface
interface AutomationRegistrarInterface {
    function registerUpkeep(RegistrationParams calldata requestParams) external returns (uint256);
}

contract Auctioneer {
	LinkTokenInterface public immutable i_link;
	AutomationRegistrarInterface public immutable i_registrar;

	// ... initialise variables, errors, events, mappings and functions

	/** 
	* When deploying Auctioneer.sol, you would need the LINK token address, Chainlink Registrar address and the Chainlink Registry address
	*/
	constructor(address _link, address _registrar, address _registry) {
        if (_link == address(0) || _registrar == address(0) || _registry == address(0)) {
            revert InvalidAddresses();
        }
        i_link = LinkTokenInterface(_link); 
        i_registrar = AutomationRegistrarInterface(_registrar);
        i_registry = _registry; 
    }

    /**
     * @notice Create a new Dutch auction
     */
    function createAuction(
        string memory _name,
        string memory _symbol,
        uint256 _totalSupply,
        uint256 _initialPrice,
        uint256 _reservePrice,
        uint256 _minimumBid
    ) external returns (address) {
        // Create new auction
        DutchAuction newAuction = new DutchAuction(
            _name, _symbol, _totalSupply, _initialPrice, _reservePrice, _minimumBid, msg.sender, address(this)
        );

        // Transfer LINK tokens from msg.sender to this contract
        if (!i_link.transferFrom(msg.sender, address(this), UPKEEP_MINIMUM_FUNDS)) {
            revert LinkTransferFailed();
        }

        // Register the upkeep
        uint256 upkeepId = _registerAuctionUpkeep(address(newAuction));

        // Setup auction with registry (not registrar)
        auctions.push(newAuction);
        isValidAuction[address(newAuction)] = true;
        auctionUpkeepIds[address(newAuction)] = upkeepId;

        emit AuctionCreated(
            address(newAuction), _name, _symbol, _totalSupply, _initialPrice, _reservePrice, _minimumBid, upkeepId
        );

        return address(newAuction);
    }

	//////////////////////////////////////////
    ///// CHAINLINK AUTOMATION FUNCTIONS /////
    //////////////////////////////////////////

	/**
     * @notice Internal function to register upkeep for an auction
     * @param auctionAddress The address of the auction to register upkeep for
     * @return upkeepId The ID of the registered upkeep
     */
    function _registerAuctionUpkeep(address auctionAddress) internal returns (uint256) {
        // Approve LINK transfer to registrar
        i_link.approve(address(i_registrar), UPKEEP_MINIMUM_FUNDS);

        // Prepare registration parameters
        RegistrationParams memory params = RegistrationParams({
            name: &quot;Dutch Auction Automation&quot;,
            encryptedEmail: bytes(&quot;&quot;),
            upkeepContract: auctionAddress,
            gasLimit: UPKEEP_GAS_LIMIT,
            adminAddress: msg.sender,
            triggerType: 0,
            checkData: bytes(&quot;&quot;),
            triggerConfig: bytes(&quot;&quot;),
            offchainConfig: bytes(&quot;&quot;),
            amount: UPKEEP_MINIMUM_FUNDS,
            billingToken: IERC20(address(i_link))
        });

        // Register upkeep
        try i_registrar.registerUpkeep(params) returns (uint256 upkeepId) {
            return upkeepId;
        } catch {
            revert UpkeepRegistrationFailed();
        }
    }
}"><code><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.26;</span>

<span class="hljs-keyword">import</span> {<span class="hljs-title">DutchAuction</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./DutchAuction.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">IERC20Metadata</span> <span class="hljs-title"><span class="hljs-keyword">as</span></span> <span class="hljs-title">IERC20</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">LinkTokenInterface</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@chainlink/contracts/src/v0.8/shared/interfaces/LinkTokenInterface.sol"</span>;

<span class="hljs-comment">// Create a RegistrationParams struct</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title">RegistrationParams</span> {
    <span class="hljs-keyword">address</span> upkeepContract;
    <span class="hljs-keyword">uint96</span> amount;
    <span class="hljs-keyword">address</span> adminAddress;
    <span class="hljs-keyword">uint32</span> gasLimit;
    <span class="hljs-keyword">uint8</span> triggerType;
    IERC20 billingToken;
    <span class="hljs-keyword">string</span> name;
    <span class="hljs-keyword">bytes</span> encryptedEmail;
    <span class="hljs-keyword">bytes</span> checkData;
    <span class="hljs-keyword">bytes</span> triggerConfig;
    <span class="hljs-keyword">bytes</span> offchainConfig;
}

<span class="hljs-comment">// Instantiate an Automation Registrar Interface</span>
<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">AutomationRegistrarInterface</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">registerUpkeep</span>(<span class="hljs-params">RegistrationParams <span class="hljs-keyword">calldata</span> requestParams</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>)</span>;
}

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Auctioneer</span> </span>{
	LinkTokenInterface <span class="hljs-keyword">public</span> <span class="hljs-keyword">immutable</span> i_link;
	AutomationRegistrarInterface <span class="hljs-keyword">public</span> <span class="hljs-keyword">immutable</span> i_registrar;

	<span class="hljs-comment">// ... initialise variables, errors, events, mappings and functions</span>

	<span class="hljs-comment">/** 
	* When deploying Auctioneer.sol, you would need the LINK token address, Chainlink Registrar address and the Chainlink Registry address
	*/</span>
	<span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _link, <span class="hljs-keyword">address</span> _registrar, <span class="hljs-keyword">address</span> _registry</span>) </span>{
        <span class="hljs-keyword">if</span> (_link <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>) <span class="hljs-operator">|</span><span class="hljs-operator">|</span> _registrar <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>) <span class="hljs-operator">|</span><span class="hljs-operator">|</span> _registry <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0</span>)) {
            <span class="hljs-keyword">revert</span> InvalidAddresses();
        }
        i_link <span class="hljs-operator">=</span> LinkTokenInterface(_link); 
        i_registrar <span class="hljs-operator">=</span> AutomationRegistrarInterface(_registrar);
        i_registry <span class="hljs-operator">=</span> _registry; 
    }

    <span class="hljs-comment">/**
     * @notice Create a new Dutch auction
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createAuction</span>(<span class="hljs-params">
        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> _name,
        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> _symbol,
        <span class="hljs-keyword">uint256</span> _totalSupply,
        <span class="hljs-keyword">uint256</span> _initialPrice,
        <span class="hljs-keyword">uint256</span> _reservePrice,
        <span class="hljs-keyword">uint256</span> _minimumBid
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">address</span></span>) </span>{
        <span class="hljs-comment">// Create new auction</span>
        DutchAuction newAuction <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> DutchAuction(
            _name, _symbol, _totalSupply, _initialPrice, _reservePrice, _minimumBid, <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>)
        );

        <span class="hljs-comment">// Transfer LINK tokens from msg.sender to this contract</span>
        <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>i_link.transferFrom(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), UPKEEP_MINIMUM_FUNDS)) {
            <span class="hljs-keyword">revert</span> LinkTransferFailed();
        }

        <span class="hljs-comment">// Register the upkeep</span>
        <span class="hljs-keyword">uint256</span> upkeepId <span class="hljs-operator">=</span> _registerAuctionUpkeep(<span class="hljs-keyword">address</span>(newAuction));

        <span class="hljs-comment">// Setup auction with registry (not registrar)</span>
        auctions.<span class="hljs-built_in">push</span>(newAuction);
        isValidAuction[<span class="hljs-keyword">address</span>(newAuction)] <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
        auctionUpkeepIds[<span class="hljs-keyword">address</span>(newAuction)] <span class="hljs-operator">=</span> upkeepId;

        <span class="hljs-keyword">emit</span> AuctionCreated(
            <span class="hljs-keyword">address</span>(newAuction), _name, _symbol, _totalSupply, _initialPrice, _reservePrice, _minimumBid, upkeepId
        );

        <span class="hljs-keyword">return</span> <span class="hljs-keyword">address</span>(newAuction);
    }

	<span class="hljs-comment">//////////////////////////////////////////</span>
    <span class="hljs-comment">///// CHAINLINK AUTOMATION FUNCTIONS /////</span>
    <span class="hljs-comment">//////////////////////////////////////////</span>

	<span class="hljs-comment">/**
     * @notice Internal function to register upkeep for an auction
     * @param auctionAddress The address of the auction to register upkeep for
     * @return upkeepId The ID of the registered upkeep
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_registerAuctionUpkeep</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> auctionAddress</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-comment">// Approve LINK transfer to registrar</span>
        i_link.approve(<span class="hljs-keyword">address</span>(i_registrar), UPKEEP_MINIMUM_FUNDS);

        <span class="hljs-comment">// Prepare registration parameters</span>
        RegistrationParams <span class="hljs-keyword">memory</span> params <span class="hljs-operator">=</span> RegistrationParams({
            name: <span class="hljs-string">"Dutch Auction Automation"</span>,
            encryptedEmail: <span class="hljs-keyword">bytes</span>(<span class="hljs-string">""</span>),
            upkeepContract: auctionAddress,
            gasLimit: UPKEEP_GAS_LIMIT,
            adminAddress: <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>,
            triggerType: <span class="hljs-number">0</span>,
            checkData: <span class="hljs-keyword">bytes</span>(<span class="hljs-string">""</span>),
            triggerConfig: <span class="hljs-keyword">bytes</span>(<span class="hljs-string">""</span>),
            offchainConfig: <span class="hljs-keyword">bytes</span>(<span class="hljs-string">""</span>),
            amount: UPKEEP_MINIMUM_FUNDS,
            billingToken: IERC20(<span class="hljs-keyword">address</span>(i_link))
        });

        <span class="hljs-comment">// Register upkeep</span>
        <span class="hljs-keyword">try</span> i_registrar.registerUpkeep(params) <span class="hljs-keyword">returns</span> (<span class="hljs-keyword">uint256</span> upkeepId) {
            <span class="hljs-keyword">return</span> upkeepId;
        } <span class="hljs-keyword">catch</span> {
            <span class="hljs-keyword">revert</span> UpkeepRegistrationFailed();
        }
    }
}</code></pre><p>** For the LINK token address, Automation Registry address and Automation Registrar address on Base Sepolia network, you can <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/dannweeeee/bid-it-contracts/blob/master/script/deployAuctioneer.s.sol">refer</a> to this <code>deployAuctioneer.s.sol</code> file.</p><p><strong>Important to note: </strong>Chainlink Automation deploys different versions of <code>AutomationRegistry.sol</code> across various chains. For example, <strong>Ethereum Sepolia</strong> uses <code>AutomationRegistry2_1.sol</code>, while <strong>Base Sepolia</strong> uses <code>AutomationRegistry2_3.sol</code>. The key difference to be aware of is that the <code>RegistrationParams</code><strong><em> struct varies between versions</em></strong>:</p><ul><li><p><code>AutomationRegistry2_1.sol</code> on <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://sepolia.etherscan.io/address/0x86efbd0b6736bed994962f9797049422a3a8e8ad">Ethereum Sepolia</a> <strong>does not require</strong> <code>billingToken</code> in the struct</p></li></ul><pre data-type="codeBlock" text="// RegistrationParams for AutomationRegistry2_1.sol
struct RegistrationParams {
    string name;
    bytes encryptedEmail;
    address upkeepContract;
    uint32 gasLimit;
    address adminAddress;
    uint8 triggerType;
    bytes checkData;
    bytes triggerConfig;
    bytes offchainConfig;
    uint96 amount;
}"><code><span class="hljs-comment">// RegistrationParams for AutomationRegistry2_1.sol</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title">RegistrationParams</span> {
    <span class="hljs-keyword">string</span> name;
    <span class="hljs-keyword">bytes</span> encryptedEmail;
    <span class="hljs-keyword">address</span> upkeepContract;
    <span class="hljs-keyword">uint32</span> gasLimit;
    <span class="hljs-keyword">address</span> adminAddress;
    <span class="hljs-keyword">uint8</span> triggerType;
    <span class="hljs-keyword">bytes</span> checkData;
    <span class="hljs-keyword">bytes</span> triggerConfig;
    <span class="hljs-keyword">bytes</span> offchainConfig;
    <span class="hljs-keyword">uint96</span> amount;
}</code></pre><ul><li><p><code>AutomationRegistry2_3.sol</code> on <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://sepolia.basescan.org/address/0x91d4a4c3d448c7f3cb477332b1c7d420a5810ac3#code">Base Sepolia</a> <strong>requires </strong><code>billingToken</code> in the struct</p></li></ul><pre data-type="codeBlock" text="// RegistrationParams for AutomationRegistry2_3.sol
struct RegistrationParams {
    address upkeepContract;
    uint96 amount;
    address adminAddress;
    uint32 gasLimit;
    uint8 triggerType;
    IERC20 billingToken;
    string name;
    bytes encryptedEmail;
    bytes checkData;
    bytes triggerConfig;
    bytes offchainConfig;
}"><code><span class="hljs-comment">// RegistrationParams for AutomationRegistry2_3.sol</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title">RegistrationParams</span> {
    <span class="hljs-keyword">address</span> upkeepContract;
    <span class="hljs-keyword">uint96</span> amount;
    <span class="hljs-keyword">address</span> adminAddress;
    <span class="hljs-keyword">uint32</span> gasLimit;
    <span class="hljs-keyword">uint8</span> triggerType;
    IERC20 billingToken;
    <span class="hljs-keyword">string</span> name;
    <span class="hljs-keyword">bytes</span> encryptedEmail;
    <span class="hljs-keyword">bytes</span> checkData;
    <span class="hljs-keyword">bytes</span> triggerConfig;
    <span class="hljs-keyword">bytes</span> offchainConfig;
}</code></pre><p>Putting it all together, <strong><em>this guide walks you through the process of creating automation-compatible smart contracts and seamlessly registering upkeeps on the Chainlink Automation Network</em></strong>. The two key contracts — <code>DutchAuction.sol</code> and <code>Auctioneer.sol</code> — work hand in hand, with <code>DutchAuction.sol</code> implementing Chainlink’s <code>checkUpkeep</code> and <code>performUpkeep</code> functions to automate auction finalization, while <code>Auctioneer.sol</code> handles programmatic upkeep registration via the <code>registerUpkeep</code> function. </p><div class="relative header-and-anchor"><h2 id="h-conclusion">Conclusion</h2></div><p>By leveraging <strong>Chainlink Automation</strong>, we can eliminate the need for manual smart contract triggers, allowing decentralized applications to function <strong>autonomously</strong> with <strong>greater reliability</strong> and <strong>efficiency</strong>. </p><p>I hope that the structured approach used in this guide is able to assist you in your journey of integrating automation into smart contracts, making it easier to <strong>deploy</strong>, <strong>manage</strong>, and <strong>scale</strong> your decentralized system. </p><p><strong><em>With a solid understanding of upkeep registration and automation logic, you are now equipped with the power to build resilient and self-sustaining smart contracts.</em></strong></p><p>Let's work towards building together a fully autonomous onchain future together. <span data-name="halo" class="emoji" data-type="emoji"><img src="https://cdn.jsdelivr.net/npm/emoji-datasource-apple/img/apple/64/1f607.png" draggable="false" loading="lazy" align="absmiddle"></span></p>]]></content:encoded>
            <author>dannweeeee@newsletter.paragraph.com (based dann)</author>
            <category>base</category>
            <category>chainlink</category>
            <category>solidity</category>
            <category>foundry</category>
            <category>open-zeppelin</category>
            <category>smartcontractkit</category>
            <category>chainlink-automation</category>
            <category>chainlink-keepers</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/6a3a7c9241d4aa5b34b8b29c42697ab1.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Develop World Class Swap Hooks with Jupiter Exchange's V6 Swap API]]></title>
            <link>https://blog.dannwee.xyz/develop-world-class-swap-hooks-with-jupiter-exchanges-v6-swap-api</link>
            <guid>oMXF2JoQZm7slE6hxoCV</guid>
            <pubDate>Wed, 15 Jan 2025 16:00:00 GMT</pubDate>
            <description><![CDATA["Increase Bandwidth, Reduce Latency", is more than just a tagline for Solana. It is a core principle that drives the network's innovation, enabling dApps built on Solana to deliver unparalleled consumer experiences. Jupiter Exchange perfectly exemplifies this vision, offering seamless and lightning-fast token swaps, making it the embodiment of Solana’s commitment to speed and efficiency. Now, what if I told you that in just 15 to 30 minutes, the Solana dApp that you’re building could seamless...]]></description>
            <content:encoded><![CDATA[<p><strong><em>"Increase Bandwidth, Reduce Latency"</em></strong>, is more than just a tagline for Solana. It is a core principle that drives the network's innovation, enabling dApps built on Solana to deliver unparalleled consumer experiences. <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://jup.ag/"><strong>Jupiter Exchange</strong></a> perfectly exemplifies this vision, offering seamless and lightning-fast token swaps, making it the embodiment of Solana’s commitment to <strong>speed</strong> and <strong>efficiency</strong>.</p><p><strong><em>Now, what if I told you that in just 15 to 30 minutes, the Solana dApp that you’re building could seamlessly integrate a Jupiter Swap feature that enables users to transact effortlessly without ever leaving the platform?</em></strong></p><p>That's right. With Jupiter's streamlined swap integration process, you can empower your users to access lightning-fast token swaps directly within your dApp. <strong>No extra steps, no unnecessary redirects. </strong>Just a smooth, intuitive experience that keeps users engaged while enhancing the functionality of your platform. </p><p>In this Builder Guide, I'll walk you through how you can <strong>create from scratch a Swap feature that enables seamless swaps on your platform</strong> while also covering how you can successfully land transactions onchain with Jupiter. Additionally, I'll show how you can activate a <strong>referral fee feature</strong>, allowing your platform to earn a fee on every swap transaction made through it. Whether you’re an established Solana dApp or just starting your journey, this guide will provide you with the tools and insights to build a world-class user experience.</p><div class="relative header-and-anchor"><h2 id="h-how-jupiter-swap-works">How Jupiter Swap Works</h2></div><p>Fundamentally, Jupiter Swap is powered by an intricate routing engine, named Metis, designed to handle Solana's numerous AMMs and provide users with the best swap prices while operating seamlessly on Solana's hyper-fast block times.</p><p>The diagram below illustrates the evolution of Jupiter Swaps, progressing from <strong><em>V1 to V2 to Metis</em></strong>, growing from <strong><em>single-route swaps </em></strong><em>to</em><strong><em> multi-route swaps </em></strong><em>to</em><strong><em> incremental-route swaps.</em></strong> Metis optimizes swap prices by streaming input tokens to incrementally construct routes that can split and merge at any stage. This iterative approach allows routes to be generated sequentially for each split, enabling the reuse of the same / different DEX across different splits. As a result, Jupiter can dynamically evaluate route prices, ensuring users receive the best possible swap price.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/63c6ce9c1388bb656930cc190b6fb0f7.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAASCAIAAAC1qksFAAAACXBIWXMAAAsTAAALEwEAmpwYAAAE10lEQVR4nH1V248TVRw+7dw6nc7OtLPnnJnOzOncznSm7V673d3uJezNhZVdhV1u4oohm+BiUOQSsoEIiEbCC6IJwRf1LyAmxvhmfDMaEyMSFI0JDyT+D75Y02mRhQSS83AyD79vvt93OUBGBFOqe6HhRVloy4jIiGShzahFCRIJORJ2stjJIkdALqs5LHQF7AnY41GQXCinhzymKdEGogMYB8gBp4ds8p3FFMh6SZ9qVkfG++oTWWh2pvfa1Ks18mYgwlIbAzlZ5ArIteJhKx4W9QQA+8mhPAq4YsQd2ahurq9srTZWJoHaxUgAkJXTbFbVWVWXkdUByCHq1aZFLRC1hESbh5tSiB2O2+VmquCIusejxwCsXQXnb4Ar769+dm7h+G4g+U8AyMhSMFEw6QDkNLu+MDj/6vjo7hEZOxJ0ZOxwBceI45mDzR2HJqxahdE6W/J5HAg6TRU8rz4wsz7TXJuS7JiBQWf6Y4Bth8iQfP7jxr3Wpdt/nUA+5QtOT9EDjLV8bPbn1rt3WhcPnF4EHBGNLgPRCAHnnbm1717r4vf/bNWXmkD0eaP8TAAJErNM43pkx7S7H+RkoNtj+nSoQodqik15tE0DTFkYKH5MG4P2YB9nlDn0JAOhYCXyPmKgmVmT8ieviMFAVrUk7LaNVPQ52eT2HBX2H+NVIhphe3p7RVQ0ylkzZrQgvbGVnl1Lq65gVbj/GSjYsmLaa3mqXkpkIJJmKjbVj2yqtC9TsGXsZiDJIl9QLXX+JWVxNaM5HPRY6PI4YKHf9mviVHF5XRhZ4GHAGREDg44SQDXsvmZEaEXVS4mRiIJLnKwDAIDQ22P4qZwezuy6/uDh3ImzAOQB0A59dPODX+/B/nEA8jvePH3j4d/+7LJgxh/+cvfwjVsAaPuv37z62x+9tUmgOiCHiVxpWLTqlvtJWMthwsmYNCbXP74VzexiFEOCpbxfHVp7zeif4KDLI6/UfKG2vF+yIqAS2N8cOXC0Nx5jUBAtHdBH59OaTyaW6M41wawwKGgD9MaDuhdhEkKb9uhOOgvLszvPffvd0OoraVlnlGIwvXjt9z93HD8FOCwalNUcIJus5go4SBfctOqIeqj3T7391Tcrl6+xBX/n1nsnbn+dj8dAwQP5ojf44h43GsgbLl8wtq1ITolI1l1RI3k3riztRZUGq1ocdLPFUDKjtsLIzxhhzq4y0M+5A3PvnB9Yex1IpPry4fnTFyRvMA39touURN6exEU5TCStqAZV6+onaq3RcVEGEq5AhJ4iOvoW2jjVg71sMXyU5CBrxhmzImCa3jyXWdjLJ/ICzkxD/1lBs9mCy+7Yy2Eqana3iwyfVazUwCwYXmAUIuodj3ZzIBploAZgahVE06mCxxfj5wUtp9lxszaxNFSdrLWrIilUXnO1IGws1sd21VE5YuETVcFovlnrH10aG5xvyKWIeSpoTycZkS9+euN+6/KXD04inwoFR0mqYmVz7k7r4t3WpYNntldFkFSFe/bTffdbl3/490JSFd7zGGShTYei4bmhcLTa3Q92MppbcIPq7HDfbL3XD7cz4HXKwADHtdr8WHl6RLSiNOr+/jM0QIRRTCBYadmWUPs9yCbvAau5QCRAKnWnd7so4JO3BWgBEH0g+9uns5j+B++G9T5GyHVbAAAAAElFTkSuQmCC" nextheight="675" nextwidth="1200" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><div class="relative header-and-anchor"><h2 id="h-overview-of-jupiters-apis">Overview of Jupiter's APIs</h2></div><div class="relative header-and-anchor"><h3 id="h-jupiter-swap-api-httpsquote-apijupagv6swap">Jupiter Swap API - <code>https://quote-api.jup.ag/v6/swap</code></h3></div><p>The <strong>Jupiter Swap API</strong> facilitates seamless token swaps on the Solana blockchain by enabling developers to interact with liquidity pools efficiently. Upon receiving these inputs, the API returns a serialized transaction that can be signed and broadcasted to the network. You can enhance transaction success by leveraging advanced features like priority fees, compute unit limits, and MEV protection. </p><p>The following JSON represents the payload for a <code>POST</code> /swap endpoint request:</p><pre data-type="codeBlock" text="{
  // The public key of the user making the request (REQUIRED)
  &quot;userPublicKey&quot;: &quot;string&quot;,

  // Automatically wrap/unwrap SOL if true; use wSOL token account otherwise (default: true)
  &quot;wrapAndUnwrapSol&quot;: true,

  // Enable shared program accounts; required if using destinationTokenAccount
  &quot;useSharedAccounts&quot;: true,

  // Fee token account; must be created beforehand if feeBps is set
  &quot;feeAccount&quot;: &quot;string&quot;,

  // A public key used to track transactions (useful for integrators)
  &quot;trackingAccount&quot;: &quot;string&quot;,

  // Compute unit price for prioritizing the transaction (in micro lamports)
  &quot;computeUnitPriceMicroLamports&quot;: 0,

  // Additional fee for prioritizing the transaction in lamports
  &quot;prioritizationFeeLamports&quot;: 0,

  // Request a legacy transaction instead of a versioned transaction (default: false)
  &quot;asLegacyTransaction&quot;: false,

  // Use the token ledger to calculate token input based on differences (default: false)
  &quot;useTokenLedger&quot;: false,

  // Public key of the token account to receive the output tokens
  &quot;destinationTokenAccount&quot;: &quot;string&quot;,

  // Enable dynamic compute unit limits using swap simulation (default: false)
  &quot;dynamicComputeUnitLimit&quot;: true,

  // Skip RPC calls to check user's accounts; assumes all accounts are set up
  &quot;skipUserAccountsRpcCalls&quot;: true,

  // Dynamic slippage settings for trade optimization
  &quot;dynamicSlippage&quot;: {
    // Minimum slippage tolerance in basis points (bps)
    &quot;minBps&quot;: 0,

    // Maximum slippage tolerance in basis points (bps)
    &quot;maxBps&quot;: 0
  },

  // Response from a quote including details of the transaction
  &quot;quoteResponse&quot;: {
    // Mint address of the input token
    &quot;inputMint&quot;: &quot;string&quot;,

    // Amount of input tokens
    &quot;inAmount&quot;: &quot;string&quot;,

    // Mint address of the output token
    &quot;outputMint&quot;: &quot;string&quot;,

    // Amount of output tokens
    &quot;outAmount&quot;: &quot;string&quot;,

    // Threshold amount for the trade
    &quot;otherAmountThreshold&quot;: &quot;string&quot;,

    // Swap mode (e.g., ExactIn or ExactOut)
    &quot;swapMode&quot;: &quot;ExactIn&quot;,

    // Slippage in basis points
    &quot;slippageBps&quot;: 0,

    // Platform fee details
    &quot;platformFee&quot;: {
      // Amount of the fee
      &quot;amount&quot;: &quot;string&quot;,

      // Fee in basis points
      &quot;feeBps&quot;: 0
    },

    // Percentage impact on price due to the trade
    &quot;priceImpactPct&quot;: &quot;string&quot;,

    // Route plan details for the trade
    &quot;routePlan&quot;: [
      {
        &quot;swapInfo&quot;: {
          // Key of the automated market maker (AMM)
          &quot;ammKey&quot;: &quot;string&quot;,

          // Label identifying the AMM
          &quot;label&quot;: &quot;string&quot;,

          // Mint address of the input token
          &quot;inputMint&quot;: &quot;string&quot;,

          // Mint address of the output token
          &quot;outputMint&quot;: &quot;string&quot;,

          // Input token amount
          &quot;inAmount&quot;: &quot;string&quot;,

          // Output token amount
          &quot;outAmount&quot;: &quot;string&quot;,

          // Fee amount for the trade
          &quot;feeAmount&quot;: &quot;string&quot;,

          // Mint address of the fee token
          &quot;feeMint&quot;: &quot;string&quot;
        },

        // Percentage of the total trade allocated to this route
        &quot;percent&quot;: 0
      }
    ],

    // Slot context of the quote
    &quot;contextSlot&quot;: 0,

    // Time taken for the quote in milliseconds
    &quot;timeTaken&quot;: 0
  }
}"><code><span class="hljs-punctuation">{</span>
  <span class="hljs-comment">// The public key of the user making the request (REQUIRED)</span>
  <span class="hljs-attr">"userPublicKey"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"string"</span><span class="hljs-punctuation">,</span>

  <span class="hljs-comment">// Automatically wrap/unwrap SOL if true; use wSOL token account otherwise (default: true)</span>
  <span class="hljs-attr">"wrapAndUnwrapSol"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span>

  <span class="hljs-comment">// Enable shared program accounts; required if using destinationTokenAccount</span>
  <span class="hljs-attr">"useSharedAccounts"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span>

  <span class="hljs-comment">// Fee token account; must be created beforehand if feeBps is set</span>
  <span class="hljs-attr">"feeAccount"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"string"</span><span class="hljs-punctuation">,</span>

  <span class="hljs-comment">// A public key used to track transactions (useful for integrators)</span>
  <span class="hljs-attr">"trackingAccount"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"string"</span><span class="hljs-punctuation">,</span>

  <span class="hljs-comment">// Compute unit price for prioritizing the transaction (in micro lamports)</span>
  <span class="hljs-attr">"computeUnitPriceMicroLamports"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span><span class="hljs-punctuation">,</span>

  <span class="hljs-comment">// Additional fee for prioritizing the transaction in lamports</span>
  <span class="hljs-attr">"prioritizationFeeLamports"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span><span class="hljs-punctuation">,</span>

  <span class="hljs-comment">// Request a legacy transaction instead of a versioned transaction (default: false)</span>
  <span class="hljs-attr">"asLegacyTransaction"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span>

  <span class="hljs-comment">// Use the token ledger to calculate token input based on differences (default: false)</span>
  <span class="hljs-attr">"useTokenLedger"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span>

  <span class="hljs-comment">// Public key of the token account to receive the output tokens</span>
  <span class="hljs-attr">"destinationTokenAccount"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"string"</span><span class="hljs-punctuation">,</span>

  <span class="hljs-comment">// Enable dynamic compute unit limits using swap simulation (default: false)</span>
  <span class="hljs-attr">"dynamicComputeUnitLimit"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span>

  <span class="hljs-comment">// Skip RPC calls to check user's accounts; assumes all accounts are set up</span>
  <span class="hljs-attr">"skipUserAccountsRpcCalls"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span>

  <span class="hljs-comment">// Dynamic slippage settings for trade optimization</span>
  <span class="hljs-attr">"dynamicSlippage"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
    <span class="hljs-comment">// Minimum slippage tolerance in basis points (bps)</span>
    <span class="hljs-attr">"minBps"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span><span class="hljs-punctuation">,</span>

    <span class="hljs-comment">// Maximum slippage tolerance in basis points (bps)</span>
    <span class="hljs-attr">"maxBps"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span>
  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>

  <span class="hljs-comment">// Response from a quote including details of the transaction</span>
  <span class="hljs-attr">"quoteResponse"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
    <span class="hljs-comment">// Mint address of the input token</span>
    <span class="hljs-attr">"inputMint"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"string"</span><span class="hljs-punctuation">,</span>

    <span class="hljs-comment">// Amount of input tokens</span>
    <span class="hljs-attr">"inAmount"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"string"</span><span class="hljs-punctuation">,</span>

    <span class="hljs-comment">// Mint address of the output token</span>
    <span class="hljs-attr">"outputMint"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"string"</span><span class="hljs-punctuation">,</span>

    <span class="hljs-comment">// Amount of output tokens</span>
    <span class="hljs-attr">"outAmount"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"string"</span><span class="hljs-punctuation">,</span>

    <span class="hljs-comment">// Threshold amount for the trade</span>
    <span class="hljs-attr">"otherAmountThreshold"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"string"</span><span class="hljs-punctuation">,</span>

    <span class="hljs-comment">// Swap mode (e.g., ExactIn or ExactOut)</span>
    <span class="hljs-attr">"swapMode"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"ExactIn"</span><span class="hljs-punctuation">,</span>

    <span class="hljs-comment">// Slippage in basis points</span>
    <span class="hljs-attr">"slippageBps"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span><span class="hljs-punctuation">,</span>

    <span class="hljs-comment">// Platform fee details</span>
    <span class="hljs-attr">"platformFee"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
      <span class="hljs-comment">// Amount of the fee</span>
      <span class="hljs-attr">"amount"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"string"</span><span class="hljs-punctuation">,</span>

      <span class="hljs-comment">// Fee in basis points</span>
      <span class="hljs-attr">"feeBps"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span>
    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>

    <span class="hljs-comment">// Percentage impact on price due to the trade</span>
    <span class="hljs-attr">"priceImpactPct"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"string"</span><span class="hljs-punctuation">,</span>

    <span class="hljs-comment">// Route plan details for the trade</span>
    <span class="hljs-attr">"routePlan"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span>
      <span class="hljs-punctuation">{</span>
        <span class="hljs-attr">"swapInfo"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
          <span class="hljs-comment">// Key of the automated market maker (AMM)</span>
          <span class="hljs-attr">"ammKey"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"string"</span><span class="hljs-punctuation">,</span>

          <span class="hljs-comment">// Label identifying the AMM</span>
          <span class="hljs-attr">"label"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"string"</span><span class="hljs-punctuation">,</span>

          <span class="hljs-comment">// Mint address of the input token</span>
          <span class="hljs-attr">"inputMint"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"string"</span><span class="hljs-punctuation">,</span>

          <span class="hljs-comment">// Mint address of the output token</span>
          <span class="hljs-attr">"outputMint"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"string"</span><span class="hljs-punctuation">,</span>

          <span class="hljs-comment">// Input token amount</span>
          <span class="hljs-attr">"inAmount"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"string"</span><span class="hljs-punctuation">,</span>

          <span class="hljs-comment">// Output token amount</span>
          <span class="hljs-attr">"outAmount"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"string"</span><span class="hljs-punctuation">,</span>

          <span class="hljs-comment">// Fee amount for the trade</span>
          <span class="hljs-attr">"feeAmount"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"string"</span><span class="hljs-punctuation">,</span>

          <span class="hljs-comment">// Mint address of the fee token</span>
          <span class="hljs-attr">"feeMint"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"string"</span>
        <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>

        <span class="hljs-comment">// Percentage of the total trade allocated to this route</span>
        <span class="hljs-attr">"percent"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span>
      <span class="hljs-punctuation">}</span>
    <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span>

    <span class="hljs-comment">// Slot context of the quote</span>
    <span class="hljs-attr">"contextSlot"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span><span class="hljs-punctuation">,</span>

    <span class="hljs-comment">// Time taken for the quote in milliseconds</span>
    <span class="hljs-attr">"timeTaken"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span>
  <span class="hljs-punctuation">}</span>
<span class="hljs-punctuation">}</span></code></pre><div class="relative header-and-anchor"><h3 id="h-jupiter-quote-api-httpsquote-apijupagv6quote">Jupiter Quote API - <code>https://quote-api.jup.ag/v6/quote</code></h3></div><p>The <strong>Jupiter Quote API </strong>is used to obtain optimal token swap routes and pricing. The API provides a detailed route plan, price impact, and fee breakdown to help users evaluate their swaps before execution.</p><p><strong>Required Parameters:</strong></p><ul><li><p><code>inputMint</code> <strong>(string)</strong>: The mint address of the input token.</p></li><li><p><code>outputMint</code> <strong>(string)</strong>: The mint address of the output token.</p></li><li><p><code>amount</code> <strong>(integer)</strong>: The amount of input tokens in the smallest unit (e.g. lamports for SOL).</p></li></ul><p><strong>Optional Parameters:</strong></p><ul><li><p><code>slippageBps</code><strong> (integer)</strong>: Defaults to 50 basis points (0.5%). Specifies the maximum slippage allowed for the swap.</p></li><li><p><code>dexes</code><strong> (string[]): </strong>Defaults to including all available decentralized exchanges (DEXes). You can specify an array of DEX names to include only selected DEXes.</p></li><li><p><code>excludeDexes</code><strong> (string[]): </strong>Excludes specific decentralized exchanges from the routing. (e.g. ["DEX1", "DEX2"]).</p></li><li><p><code>swapMode</code><strong> (string): </strong><code>ExactIn</code> (default) for swaps with a fixed input amount. <code>ExactOut</code> for swaps requiring an exact output amount (useful for payments).</p></li><li><p><code>restrictIntermediateTokens</code><strong> (boolean): </strong>Defaults to false. Restricts intermediate tokens to highly liquid pairs. This also helps to increase the chances of your transaction landing onchain.</p></li><li><p><code>onlyDirectRoutes</code><strong> (boolean): </strong>Defaults to false. If set to true, the routing is restricted to single-hop routes (direct swaps) only.</p></li><li><p><code>asLegacyTransaction</code><strong> (boolean): </strong>Defaults to false. When enabled, the API uses legacy transactions instead of versioned transactions.</p></li><li><p><code>platformFeeBps</code><strong> (integer): </strong>Specifies a platform fee in basis points (BPS) to be deducted from the output token amount.</p></li><li><p><code>maxAccounts</code><strong> (integer): </strong>Allows you to limit the maximum number of accounts involved in the quote for easier integration with your own accounts.</p></li><li><p><code>autoSlippage</code><strong> (boolean): </strong>Defaults to false. If enabled, the API provides a suggested smart slippage value to optimize the swap process.</p></li><li><p><code>autoSlippageCollisionUsdValue</code><strong> (integer): </strong>Defaults to 1000. When autoSlippage is enabled, this parameter defines the USD value used to calculate the smart slippage impact. You can set a custom USD value here.</p></li><li><p><code>maxAutoSlippageBps</code><strong> (integer): </strong>Works in conjunction with autoSlippage=true. Specifies the maximum slippageBps allowed when using smart slippage. Setting this ensures the slippage value remains within a reasonable range.</p></li></ul><div class="relative header-and-anchor"><h3 id="h-jupiter-tokens-api-httpstokensjupagtokens">Jupiter Tokens API - <code>https://tokens.jup.ag/tokens</code></h3></div><p>The <strong>Jupiter Tokens API </strong>provides detailed information about tokens available in the Jupiter ecosystem. It offers flexibility for retrieving token data by tag, mint address, or liquidity/market thresholds. Here are the details of the parameters and endpoints:</p><p><strong>GET Tokens by Tag - </strong><code>https://tokens.jup.ag/tokens?tags=</code></p><ul><li><p>Retrieves tokens filtered by specific tags.</p></li></ul><ul><li><p><strong>Convenience Tags:</strong></p><ul><li><p><code>verified</code>: Tokens verified and trusted by Jupiter. Combines “community” and “lst” tags.</p></li><li><p><code>unknown</code>: Tokens flagged as unverified or requiring caution.</p></li></ul></li><li><p><strong>Other Tags:</strong></p><ul><li><p><code>community</code>: Tokens approved by the Jupiter community.</p></li><li><p><code>strict</code>: Previously validated tokens from deprecated strict-list repositories.</p></li><li><p><code>lst</code>: Tokens from <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://app.sanctum.so/lsts">Sanctum’s list</a>.</p></li><li><p><code>birdeye-trending</code>: Top 100 trending tokens from BirdEye.</p></li><li><p><code>clone</code>: Tokens listed by Clone protocol.</p></li><li><p><code>pump</code>: Tokens that graduated from the Pump protocol.</p></li></ul></li></ul><p><strong>GET Token by Mint - </strong><code>https://tokens.jup.ag/token/{mint_address}</code></p><ul><li><p>Retrieves metadata for a specific token by its mint address.</p></li></ul><ul><li><p><code>mint_address</code> <strong>(string)</strong>: The token’s unique mint address.</p></li></ul><p><strong>GET Tradable Tokens - </strong><code>https://tokens.jup.ag/tokens_with_markets</code></p><ul><li><p>Fetches a list of tokens tradable on Jupiter, meeting liquidity and routing thresholds.</p></li></ul><div class="relative header-and-anchor"><h2 id="h-step-by-step-guide-equipping-your-dapp-with-jupiter-swap">Step-By-Step Guide: Equipping your dApp with Jupiter Swap</h2></div><p>To provide context, in this guide we will be focusing mainly on the implementation of <strong>Typescript Hooks</strong> with <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://station.jup.ag/docs/apis/swap-api"><strong>Jupiter's V6 Swap API</strong></a><strong> </strong>while utilizing <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.npmjs.com/package/zustand"><strong>Zustand</strong></a> for persistent state management. </p><p><strong><em>NOTE: If you would like an even quicker and easier implementation with the user interface done up, you can go through the following resources:</em></strong></p><ol><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://station.jup.ag/docs/jupiter-terminal/jupiter-terminal"><strong><em>Jupiter Terminal</em></strong></a><em> - an open-source plug-and-play terminal that can be embedded in your dApp as an integrated swap OR widget OR modal. (</em>An article will be released in the future covering this<em>)</em></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://jupiversekit.vercel.app/"><strong><em>Jupiverse Kit</em></strong></a><strong><em> </em></strong><em>- an open-source passion project kickstarted by </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/dannweeeee"><em>myself</em></a><em> to equip Solana developers with a ready-to-use React components library powered by Jupiter. (</em>An article will be released in the future covering this<em>)</em></p></li></ol><p>Now back to building <span data-name="drooling_face" class="emoji" data-type="emoji"><img src="https://cdn.jsdelivr.net/npm/emoji-datasource-apple/img/apple/64/1f924.png" draggable="false" loading="lazy" align="absmiddle"></span>.</p><p>In essence, accessing liquidity on Solana has never been easier, thanks to Jupiter’s innovative V6 Swap API. By simply sending a <strong><em>HTTP request</em></strong> to <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://quote-api.jup.ag/v6">https://quote-api.jup.ag/v6</a> and specifying the token pairs, amount, and slippage, developers can quickly obtain serialized transactions that are ready for execution. These transactions can then be signed and submitted to the Solana blockchain with minimal effort. However, there are several prerequisites that you will have to prepare first before using the V6 Swap API.</p><div class="relative header-and-anchor"><h3 id="h-prerequisites">Prerequisites</h3></div><ul><li><p>Install <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.npmjs.com/package/@solana/wallet-adapter-react">@solana/wallet-adapter-react</a> </p></li><li><p>Install <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.npmjs.com/package/@solana/wallet-adapter-react">@solana/web3.js</a></p></li></ul><div class="relative header-and-anchor"><h3 id="h-create-an-interfacests-to-store-all-typescript-interfaces">Create an <code>interfaces.ts</code> to store all TypeScript interfaces</h3></div><pre data-type="codeBlock" text="export interface Token {
  address: string;
  name: string;
  symbol: string;
  decimals: number;
  logoURI: string;
  tags: string[];
  daily_volume: number;
  freeze_authority: string | null;
  mint_authority: string | null;
}

export interface QuoteResponse {
  inputMint: string;
  outputMint: string;
  amount: string;
  swapMode: &quot;ExactIn&quot; | &quot;ExactOut&quot;;
  slippageBps: number;
  otherAmountThreshold: string;
  routes: any[];
  contextSlot: number;
}

export interface SwapResponse {
  swapTransaction: string;
}

export interface SwapState {
  tokenFrom: Token | null;
  tokenTo: Token | null;
  amountFrom: string;
  amountTo: string;
  slippage: number;
  quoteResponse: QuoteResponse | null;
  isCalculating: boolean;
  isSwapping: boolean;
  isFromDialogOpen: boolean;
  isToDialogOpen: boolean;
  error: string | null;
}

export interface SwapActions {
  setTokenFrom: (token: Token | null) => void;
  setTokenTo: (token: Token | null) => void;
  setAmountFrom: (amount: string) => void;
  setAmountTo: (amount: string) => void;
  setSlippage: (slippage: number) => void;
  setQuoteResponse: (quote: QuoteResponse | null) => void;
  setIsCalculating: (isCalculating: boolean) => void;
  setIsSwapping: (isSwapping: boolean) => void;
  setIsFromDialogOpen: (isOpen: boolean) => void;
  setIsToDialogOpen: (isOpen: boolean) => void;
  setError: (error: string | null) => void;
  reset: () => void;
  swapTokens: () => void;
  resetAmounts: () => void;
}"><code><span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">Token</span> {
  <span class="hljs-attr">address</span>: <span class="hljs-built_in">string</span>;
  <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>;
  <span class="hljs-attr">symbol</span>: <span class="hljs-built_in">string</span>;
  <span class="hljs-attr">decimals</span>: <span class="hljs-built_in">number</span>;
  <span class="hljs-attr">logoURI</span>: <span class="hljs-built_in">string</span>;
  <span class="hljs-attr">tags</span>: <span class="hljs-built_in">string</span>[];
  <span class="hljs-attr">daily_volume</span>: <span class="hljs-built_in">number</span>;
  <span class="hljs-attr">freeze_authority</span>: <span class="hljs-built_in">string</span> | <span class="hljs-literal">null</span>;
  <span class="hljs-attr">mint_authority</span>: <span class="hljs-built_in">string</span> | <span class="hljs-literal">null</span>;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">QuoteResponse</span> {
  <span class="hljs-attr">inputMint</span>: <span class="hljs-built_in">string</span>;
  <span class="hljs-attr">outputMint</span>: <span class="hljs-built_in">string</span>;
  <span class="hljs-attr">amount</span>: <span class="hljs-built_in">string</span>;
  <span class="hljs-attr">swapMode</span>: <span class="hljs-string">"ExactIn"</span> | <span class="hljs-string">"ExactOut"</span>;
  <span class="hljs-attr">slippageBps</span>: <span class="hljs-built_in">number</span>;
  <span class="hljs-attr">otherAmountThreshold</span>: <span class="hljs-built_in">string</span>;
  <span class="hljs-attr">routes</span>: <span class="hljs-built_in">any</span>[];
  <span class="hljs-attr">contextSlot</span>: <span class="hljs-built_in">number</span>;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">SwapResponse</span> {
  <span class="hljs-attr">swapTransaction</span>: <span class="hljs-built_in">string</span>;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">SwapState</span> {
  <span class="hljs-attr">tokenFrom</span>: <span class="hljs-title class_">Token</span> | <span class="hljs-literal">null</span>;
  <span class="hljs-attr">tokenTo</span>: <span class="hljs-title class_">Token</span> | <span class="hljs-literal">null</span>;
  <span class="hljs-attr">amountFrom</span>: <span class="hljs-built_in">string</span>;
  <span class="hljs-attr">amountTo</span>: <span class="hljs-built_in">string</span>;
  <span class="hljs-attr">slippage</span>: <span class="hljs-built_in">number</span>;
  <span class="hljs-attr">quoteResponse</span>: <span class="hljs-title class_">QuoteResponse</span> | <span class="hljs-literal">null</span>;
  <span class="hljs-attr">isCalculating</span>: <span class="hljs-built_in">boolean</span>;
  <span class="hljs-attr">isSwapping</span>: <span class="hljs-built_in">boolean</span>;
  <span class="hljs-attr">isFromDialogOpen</span>: <span class="hljs-built_in">boolean</span>;
  <span class="hljs-attr">isToDialogOpen</span>: <span class="hljs-built_in">boolean</span>;
  <span class="hljs-attr">error</span>: <span class="hljs-built_in">string</span> | <span class="hljs-literal">null</span>;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">SwapActions</span> {
  <span class="hljs-attr">setTokenFrom</span>: <span class="hljs-function">(<span class="hljs-params">token: Token | <span class="hljs-literal">null</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>;
  <span class="hljs-attr">setTokenTo</span>: <span class="hljs-function">(<span class="hljs-params">token: Token | <span class="hljs-literal">null</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>;
  <span class="hljs-attr">setAmountFrom</span>: <span class="hljs-function">(<span class="hljs-params">amount: <span class="hljs-built_in">string</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>;
  <span class="hljs-attr">setAmountTo</span>: <span class="hljs-function">(<span class="hljs-params">amount: <span class="hljs-built_in">string</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>;
  <span class="hljs-attr">setSlippage</span>: <span class="hljs-function">(<span class="hljs-params">slippage: <span class="hljs-built_in">number</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>;
  <span class="hljs-attr">setQuoteResponse</span>: <span class="hljs-function">(<span class="hljs-params">quote: QuoteResponse | <span class="hljs-literal">null</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>;
  <span class="hljs-attr">setIsCalculating</span>: <span class="hljs-function">(<span class="hljs-params">isCalculating: <span class="hljs-built_in">boolean</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>;
  <span class="hljs-attr">setIsSwapping</span>: <span class="hljs-function">(<span class="hljs-params">isSwapping: <span class="hljs-built_in">boolean</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>;
  <span class="hljs-attr">setIsFromDialogOpen</span>: <span class="hljs-function">(<span class="hljs-params">isOpen: <span class="hljs-built_in">boolean</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>;
  <span class="hljs-attr">setIsToDialogOpen</span>: <span class="hljs-function">(<span class="hljs-params">isOpen: <span class="hljs-built_in">boolean</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>;
  <span class="hljs-attr">setError</span>: <span class="hljs-function">(<span class="hljs-params">error: <span class="hljs-built_in">string</span> | <span class="hljs-literal">null</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>;
  <span class="hljs-attr">reset</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">void</span>;
  <span class="hljs-attr">swapTokens</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">void</span>;
  <span class="hljs-attr">resetAmounts</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">void</span>;
}</code></pre><div class="relative header-and-anchor"><h3 id="h-implement-usetokensts-a-token-hook-for-fetching-verified-tokens">Implement <code>useTokens.ts</code> - a Token Hook for Fetching Verified Tokens</h3></div><p>The <code>useTokens.ts</code> hook fetches verified tokens via <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://station.jup.ag/docs/token-list/token-list-api">Jupiter's Tokens API</a></p><pre data-type="codeBlock" text="import { useEffect, useState } from &quot;react&quot;;
import { Token } from &quot;../utils/interfaces&quot;;

export const useTokens = () => {
  const [tokens, setTokens] = useState<Token[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const fetchTokens = async () => {
      try {
        setLoading(true);
        const response = await fetch(
          &quot;https://tokens.jup.ag/tokens?tags=verified&quot;
        );
        if (!response.ok) {
          throw new Error(&quot;Failed to fetch tokens&quot;);
        }
        const data = await response.json();
        setTokens(data);
      } catch (err) {
        setError(err instanceof Error ? err.message : &quot;Failed to fetch tokens&quot;);
      } finally {
        setLoading(false);
      }
    };

    fetchTokens();
  }, []);

  return { tokens, loading, error };
};"><code><span class="hljs-keyword">import</span> { <span class="hljs-title">useEffect</span>, <span class="hljs-title">useState</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">Token</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../utils/interfaces"</span>;

export const useTokens <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  const [tokens, setTokens] <span class="hljs-operator">=</span> useState<span class="hljs-operator">&lt;</span>Token[]<span class="hljs-operator">&gt;</span>([]);
  const [loading, setLoading] <span class="hljs-operator">=</span> useState(<span class="hljs-literal">true</span>);
  const [<span class="hljs-function"><span class="hljs-keyword">error</span>, <span class="hljs-title">setError</span>] = <span class="hljs-title">useState</span>&lt;<span class="hljs-title"><span class="hljs-keyword">string</span></span> | <span class="hljs-title">null</span>&gt;(<span class="hljs-params">null</span>)</span>;

  useEffect(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    const fetchTokens <span class="hljs-operator">=</span> async () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
      <span class="hljs-keyword">try</span> {
        setLoading(<span class="hljs-literal">true</span>);
        const response <span class="hljs-operator">=</span> await fetch(
          <span class="hljs-string">"https://tokens.jup.ag/tokens?tags=verified"</span>
        );
        <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>response.ok) {
          <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Failed to fetch tokens"</span>);
        }
        const data <span class="hljs-operator">=</span> await response.json();
        setTokens(data);
      } <span class="hljs-keyword">catch</span> (err) {
        setError(err instanceof <span class="hljs-built_in">Error</span> ? err.message : <span class="hljs-string">"Failed to fetch tokens"</span>);
      } finally {
        setLoading(<span class="hljs-literal">false</span>);
      }
    };

    fetchTokens();
  }, []);

  <span class="hljs-keyword">return</span> { tokens, loading, <span class="hljs-function"><span class="hljs-keyword">error</span> }</span>;
};</code></pre><div class="relative header-and-anchor"><h3 id="h-create-useswapts-a-swap-hook-with-jupiters-v6-swap-api">Create <code>useSwap.ts</code> - a Swap Hook with Jupiter's V6 Swap API</h3></div><p>This <code>useSwap.ts</code> Hook will be the main file that interacts with Jupiter's Swap APIs, providing the following key functionalities:</p><ul><li><p><strong>Quote Retrieval </strong>- the <code>getQuote</code> function is responsible for fetching a quote for swapping a specified amount of one token for another, while taking into consideration the slippage and platform fees.</p><ul><li><p><em>Ensure that </em><code>restrictIntermediateTokens</code><em> is set to </em><strong><em>true</em></strong><em>. Without it, your route may be routed through random intermediate tokens, leading to higher chances of failure. </em><strong><em>Enabling this option ensures your route only uses highly liquid intermediate tokens, providing you with the best price and a more stable transaction path.</em></strong></p></li></ul></li><li><p><strong>Swap Execution </strong>- the <code>executeSwap</code> function is in charge of preparing and executing a swap transaction.</p><ul><li><p><em>You can access the </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://referral.jup.ag/dashboard"><em>Jupiter Referral Dashboard</em></a><em> to create your referral account and obtain your referral key. </em><strong><em>By using this key, you can earn fees on every swap transaction conducted through your platform.</em></strong></p></li></ul></li><li><p><strong>Output Calculation </strong>- the <code>calculateOutputAmount</code> function estimates the output amount of tokens received from a swap based on the input amount and current market conditions.</p></li><li><p><strong>Max Input Calculation </strong>- the <code>calculateMaxInput</code> function determines the maximum amount of a token that can be swapped, while taking into account the necessary gas fees if the token is SOL.</p></li></ul><pre data-type="codeBlock" text="import { useCallback, useMemo, useState } from &quot;react&quot;;
import { useWallet } from &quot;@solana/wallet-adapter-react&quot;;
import {
  Connection,
  LAMPORTS_PER_SOL,
  PublicKey,
  VersionedTransaction,
} from &quot;@solana/web3.js&quot;;
import { Token, QuoteResponse, SwapResponse } from &quot;../utils/interfaces&quot;;

// Minimum SOL to keep for gas fees (0.01 SOL)
const MIN_SOL_FOR_GAS = 0.01 * LAMPORTS_PER_SOL;
const WSOL_MINT = &quot;So11111111111111111111111111111111111111112&quot;;

interface UseSwapProps {
  rpcUrl: string;
  referralKey?: string;
  platformFeeBps?: number;
}

export const useSwap = ({
  rpcUrl,
  referralKey,
  platformFeeBps,
}: UseSwapProps) => {
  const { publicKey, signTransaction } = useWallet();
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  // Initialize connection with provided RPC URL
  const connection = useMemo(() => new Connection(rpcUrl), [rpcUrl]);

  // Get quote for swap
  const getQuote = useCallback(
    async (
      inputToken: Token,
      outputToken: Token,
      amount: number,
      slippageBps: number = 300
    ) => {
      try {
        setLoading(true);
        setError(null);

        const url = new URL(&quot;https://quote-api.jup.ag/v6/quote&quot;);
        url.searchParams.append(&quot;inputMint&quot;, inputToken.address);
        url.searchParams.append(&quot;outputMint&quot;, outputToken.address);
        url.searchParams.append(
          &quot;amount&quot;,
          (amount * Math.pow(10, inputToken.decimals)).toString()
        );
        url.searchParams.append(&quot;slippageBps&quot;, slippageBps.toString());
        url.searchParams.append(&quot;restrictIntermediateTokens&quot;, &quot;true&quot;);

        // Add platform fee if provided
        if (platformFeeBps) {
          url.searchParams.append(&quot;platformFeeBps&quot;, platformFeeBps.toString());
        }

        const response = await fetch(url.toString());
        const data = await response.json();

        if (!response.ok) {
          throw new Error(data.error || &quot;Failed to get quote&quot;);
        }

        return data as QuoteResponse;
      } catch (err) {
        const errorMessage =
          err instanceof Error ? err.message : &quot;Failed to get quote&quot;;
        setError(errorMessage);
        throw new Error(errorMessage);
      } finally {
        setLoading(false);
      }
    },
    [platformFeeBps]
  );

  // Execute swap
  const executeSwap = useCallback(
    async (quoteResponse: QuoteResponse) => {
      if (!publicKey || !signTransaction) {
        setError(&quot;Wallet not connected&quot;);
        return null;
      }

      try {
        setLoading(true);
        setError(null);

        let feeAccount;
        if (referralKey) {
          // Calculate fee account address
          const [feeAccountAddress] = await PublicKey.findProgramAddressSync(
            [
              Buffer.from(&quot;referral_ata&quot;),
              new PublicKey(referralKey).toBuffer(),
              new PublicKey(quoteResponse.inputMint).toBuffer(),
            ],
            new PublicKey(&quot;REFER4ZgmyYx9c6He5XfaTMiGfdLwRnkV4RPp9t9iF3&quot;)
          );
          feeAccount = feeAccountAddress.toString();
        }

        const swapResponse = await fetch(&quot;https://quote-api.jup.ag/v6/swap&quot;, {
          method: &quot;POST&quot;,
          headers: {
            &quot;Content-Type&quot;: &quot;application/json&quot;,
          },
          body: JSON.stringify({
            quoteResponse,
            userPublicKey: publicKey.toString(),
            wrapAndUnwrapSol: true,
            dynamicComputeUnitLimit: true,
            feeAccount,
            dynamicSlippage: {
              maxBps: quoteResponse.slippageBps,
            },
            prioritizationFeeLamports: {
              priorityLevelWithMaxLamports: {
                maxLamports: 10000000,
                priorityLevel: &quot;veryHigh&quot;,
              },
            },
          }),
        });

        if (!swapResponse.ok) {
          throw new Error(&quot;Failed to prepare swap transaction&quot;);
        }

        const { swapTransaction } = (await swapResponse.json()) as SwapResponse;

        // Deserialize the transaction
        const swapTransactionBuf = Buffer.from(swapTransaction, &quot;base64&quot;);
        const transaction = VersionedTransaction.deserialize(
          new Uint8Array(swapTransactionBuf.toJSON().data)
        );

        // Sign the transaction
        const signedTransaction = await signTransaction(transaction);

        // Execute the transaction
        const rawTransaction = signedTransaction.serialize();
        const txid = await connection.sendRawTransaction(rawTransaction, {
          skipPreflight: true,
          maxRetries: 2,
        });

        // Get the latest blockhash
        const latestBlockhash = await connection.getLatestBlockhash();

        // Confirm transaction
        await connection.confirmTransaction({
          blockhash: latestBlockhash.blockhash,
          lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
          signature: txid,
        });

        return txid;
      } catch (err) {
        const errorMessage =
          err instanceof Error ? err.message : &quot;Failed to execute swap&quot;;
        setError(errorMessage);
        throw new Error(errorMessage);
      } finally {
        setLoading(false);
      }
    },
    [connection, publicKey, signTransaction, referralKey]
  );

  // Calculate output amount based on input
  const calculateOutputAmount = useCallback(
    async (inputToken: Token, outputToken: Token, inputAmount: number) => {
      if (!inputAmount || inputAmount <= 0) return &quot;0&quot;;

      try {
        const quote = await getQuote(inputToken, outputToken, inputAmount);
        if (!quote) return &quot;0&quot;;

        // Convert amount from base units to decimal
        const outputAmount = (
          parseInt(quote.otherAmountThreshold) /
          Math.pow(10, outputToken.decimals)
        ).toString();

        return outputAmount;
      } catch (err) {
        const errorMessage =
          err instanceof Error
            ? err.message
            : &quot;Failed to calculate output amount&quot;;
        console.error(&quot;Failed to calculate output amount:&quot;, err);
        setError(errorMessage);
        throw new Error(errorMessage);
      }
    },
    [getQuote]
  );

  // Calculate max input amount accounting for gas fees if token is SOL
  const calculateMaxInput = useCallback(
    async (inputToken: Token, balance: number) => {
      if (!balance || balance <= 0) return 0;

      // If input token is SOL, leave some for gas
      if (inputToken.address === WSOL_MINT) {
        const maxAmount = Math.max(
          0,
          (balance * LAMPORTS_PER_SOL - MIN_SOL_FOR_GAS) / LAMPORTS_PER_SOL
        );
        return parseFloat(maxAmount.toFixed(inputToken.decimals));
      }

      // For other tokens, use full balance
      return balance;
    },
    []
  );

  return {
    getQuote,
    executeSwap,
    calculateOutputAmount,
    calculateMaxInput,
    loading,
    error,
  };
};"><code><span class="hljs-keyword">import</span> { <span class="hljs-title">useCallback</span>, <span class="hljs-title">useMemo</span>, <span class="hljs-title">useState</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">useWallet</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@solana/wallet-adapter-react"</span>;
<span class="hljs-keyword">import</span> {
  <span class="hljs-title">Connection</span>,
  <span class="hljs-title">LAMPORTS_PER_SOL</span>,
  <span class="hljs-title">PublicKey</span>,
  <span class="hljs-title">VersionedTransaction</span>,
} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@solana/web3.js"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">Token</span>, <span class="hljs-title">QuoteResponse</span>, <span class="hljs-title">SwapResponse</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../utils/interfaces"</span>;

<span class="hljs-comment">// Minimum SOL to keep for gas fees (0.01 SOL)</span>
const MIN_SOL_FOR_GAS <span class="hljs-operator">=</span> <span class="hljs-number">0</span><span class="hljs-number">.01</span> <span class="hljs-operator">*</span> LAMPORTS_PER_SOL;
const WSOL_MINT <span class="hljs-operator">=</span> <span class="hljs-string">"So11111111111111111111111111111111111111112"</span>;

<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">UseSwapProps</span> </span>{
  rpcUrl: <span class="hljs-keyword">string</span>;
  referralKey?: <span class="hljs-keyword">string</span>;
  platformFeeBps?: number;
}

export const useSwap <span class="hljs-operator">=</span> ({
  rpcUrl,
  referralKey,
  platformFeeBps,
}: UseSwapProps) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  const { publicKey, signTransaction } <span class="hljs-operator">=</span> useWallet();
  const [loading, setLoading] <span class="hljs-operator">=</span> useState(<span class="hljs-literal">false</span>);
  const [<span class="hljs-function"><span class="hljs-keyword">error</span>, <span class="hljs-title">setError</span>] = <span class="hljs-title">useState</span>&lt;<span class="hljs-title"><span class="hljs-keyword">string</span></span> | <span class="hljs-title">null</span>&gt;(<span class="hljs-params">null</span>)</span>;

  <span class="hljs-comment">// Initialize connection with provided RPC URL</span>
  const connection <span class="hljs-operator">=</span> useMemo(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> <span class="hljs-keyword">new</span> Connection(rpcUrl), [rpcUrl]);

  <span class="hljs-comment">// Get quote for swap</span>
  const getQuote <span class="hljs-operator">=</span> useCallback(
    async (
      inputToken: Token,
      outputToken: Token,
      amount: number,
      slippageBps: number <span class="hljs-operator">=</span> <span class="hljs-number">300</span>
    ) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
      <span class="hljs-keyword">try</span> {
        setLoading(<span class="hljs-literal">true</span>);
        setError(null);

        const url <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> URL(<span class="hljs-string">"https://quote-api.jup.ag/v6/quote"</span>);
        url.searchParams.append(<span class="hljs-string">"inputMint"</span>, inputToken.<span class="hljs-built_in">address</span>);
        url.searchParams.append(<span class="hljs-string">"outputMint"</span>, outputToken.<span class="hljs-built_in">address</span>);
        url.searchParams.append(
          <span class="hljs-string">"amount"</span>,
          (amount <span class="hljs-operator">*</span> Math.pow(<span class="hljs-number">10</span>, inputToken.decimals)).toString()
        );
        url.searchParams.append(<span class="hljs-string">"slippageBps"</span>, slippageBps.toString());
        url.searchParams.append(<span class="hljs-string">"restrictIntermediateTokens"</span>, <span class="hljs-string">"true"</span>);

        <span class="hljs-comment">// Add platform fee if provided</span>
        <span class="hljs-keyword">if</span> (platformFeeBps) {
          url.searchParams.append(<span class="hljs-string">"platformFeeBps"</span>, platformFeeBps.toString());
        }

        const response <span class="hljs-operator">=</span> await fetch(url.toString());
        const data <span class="hljs-operator">=</span> await response.json();

        <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>response.ok) {
          <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(data.error <span class="hljs-operator">|</span><span class="hljs-operator">|</span> <span class="hljs-string">"Failed to get quote"</span>);
        }

        <span class="hljs-keyword">return</span> data <span class="hljs-keyword">as</span> QuoteResponse;
      } <span class="hljs-keyword">catch</span> (err) {
        const errorMessage <span class="hljs-operator">=</span>
          err instanceof <span class="hljs-built_in">Error</span> ? err.message : <span class="hljs-string">"Failed to get quote"</span>;
        setError(errorMessage);
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(errorMessage);
      } finally {
        setLoading(<span class="hljs-literal">false</span>);
      }
    },
    [platformFeeBps]
  );

  <span class="hljs-comment">// Execute swap</span>
  const executeSwap <span class="hljs-operator">=</span> useCallback(
    async (quoteResponse: QuoteResponse) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
      <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>publicKey <span class="hljs-operator">|</span><span class="hljs-operator">|</span> <span class="hljs-operator">!</span>signTransaction) {
        setError(<span class="hljs-string">"Wallet not connected"</span>);
        <span class="hljs-keyword">return</span> null;
      }

      <span class="hljs-keyword">try</span> {
        setLoading(<span class="hljs-literal">true</span>);
        setError(null);

        let feeAccount;
        <span class="hljs-keyword">if</span> (referralKey) {
          <span class="hljs-comment">// Calculate fee account address</span>
          const [feeAccountAddress] <span class="hljs-operator">=</span> await PublicKey.findProgramAddressSync(
            [
              Buffer.from(<span class="hljs-string">"referral_ata"</span>),
              <span class="hljs-keyword">new</span> PublicKey(referralKey).toBuffer(),
              <span class="hljs-keyword">new</span> PublicKey(quoteResponse.inputMint).toBuffer(),
            ],
            <span class="hljs-keyword">new</span> PublicKey(<span class="hljs-string">"REFER4ZgmyYx9c6He5XfaTMiGfdLwRnkV4RPp9t9iF3"</span>)
          );
          feeAccount <span class="hljs-operator">=</span> feeAccountAddress.toString();
        }

        const swapResponse <span class="hljs-operator">=</span> await fetch(<span class="hljs-string">"https://quote-api.jup.ag/v6/swap"</span>, {
          method: <span class="hljs-string">"POST"</span>,
          headers: {
            <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
          },
          body: JSON.stringify({
            quoteResponse,
            userPublicKey: publicKey.toString(),
            wrapAndUnwrapSol: <span class="hljs-literal">true</span>,
            dynamicComputeUnitLimit: <span class="hljs-literal">true</span>,
            feeAccount,
            dynamicSlippage: {
              maxBps: quoteResponse.slippageBps,
            },
            prioritizationFeeLamports: {
              priorityLevelWithMaxLamports: {
                maxLamports: <span class="hljs-number">10000000</span>,
                priorityLevel: <span class="hljs-string">"veryHigh"</span>,
              },
            },
          }),
        });

        <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>swapResponse.ok) {
          <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Failed to prepare swap transaction"</span>);
        }

        const { swapTransaction } <span class="hljs-operator">=</span> (await swapResponse.json()) <span class="hljs-keyword">as</span> SwapResponse;

        <span class="hljs-comment">// Deserialize the transaction</span>
        const swapTransactionBuf <span class="hljs-operator">=</span> Buffer.from(swapTransaction, <span class="hljs-string">"base64"</span>);
        const transaction <span class="hljs-operator">=</span> VersionedTransaction.deserialize(
          <span class="hljs-keyword">new</span> Uint8Array(swapTransactionBuf.toJSON().data)
        );

        <span class="hljs-comment">// Sign the transaction</span>
        const signedTransaction <span class="hljs-operator">=</span> await signTransaction(transaction);

        <span class="hljs-comment">// Execute the transaction</span>
        const rawTransaction <span class="hljs-operator">=</span> signedTransaction.serialize();
        const txid <span class="hljs-operator">=</span> await connection.sendRawTransaction(rawTransaction, {
          skipPreflight: <span class="hljs-literal">true</span>,
          maxRetries: <span class="hljs-number">2</span>,
        });

        <span class="hljs-comment">// Get the latest blockhash</span>
        const latestBlockhash <span class="hljs-operator">=</span> await connection.getLatestBlockhash();

        <span class="hljs-comment">// Confirm transaction</span>
        await connection.confirmTransaction({
          <span class="hljs-built_in">blockhash</span>: latestBlockhash.blockhash,
          lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
          signature: txid,
        });

        <span class="hljs-keyword">return</span> txid;
      } <span class="hljs-keyword">catch</span> (err) {
        const errorMessage <span class="hljs-operator">=</span>
          err instanceof <span class="hljs-built_in">Error</span> ? err.message : <span class="hljs-string">"Failed to execute swap"</span>;
        setError(errorMessage);
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(errorMessage);
      } finally {
        setLoading(<span class="hljs-literal">false</span>);
      }
    },
    [connection, publicKey, signTransaction, referralKey]
  );

  <span class="hljs-comment">// Calculate output amount based on input</span>
  const calculateOutputAmount <span class="hljs-operator">=</span> useCallback(
    async (inputToken: Token, outputToken: Token, inputAmount: number) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
      <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>inputAmount <span class="hljs-operator">|</span><span class="hljs-operator">|</span> inputAmount <span class="hljs-operator">&lt;</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span> <span class="hljs-string">"0"</span>;

      <span class="hljs-keyword">try</span> {
        const quote <span class="hljs-operator">=</span> await getQuote(inputToken, outputToken, inputAmount);
        <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>quote) <span class="hljs-keyword">return</span> <span class="hljs-string">"0"</span>;

        <span class="hljs-comment">// Convert amount from base units to decimal</span>
        const outputAmount <span class="hljs-operator">=</span> (
          parseInt(quote.otherAmountThreshold) <span class="hljs-operator">/</span>
          Math.pow(<span class="hljs-number">10</span>, outputToken.decimals)
        ).toString();

        <span class="hljs-keyword">return</span> outputAmount;
      } <span class="hljs-keyword">catch</span> (err) {
        const errorMessage <span class="hljs-operator">=</span>
          err instanceof <span class="hljs-built_in">Error</span>
            ? err.message
            : <span class="hljs-string">"Failed to calculate output amount"</span>;
        console.error(<span class="hljs-string">"Failed to calculate output amount:"</span>, err);
        setError(errorMessage);
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(errorMessage);
      }
    },
    [getQuote]
  );

  <span class="hljs-comment">// Calculate max input amount accounting for gas fees if token is SOL</span>
  const calculateMaxInput <span class="hljs-operator">=</span> useCallback(
    async (inputToken: Token, balance: number) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
      <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>balance <span class="hljs-operator">|</span><span class="hljs-operator">|</span> balance <span class="hljs-operator">&lt;</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;

      <span class="hljs-comment">// If input token is SOL, leave some for gas</span>
      <span class="hljs-keyword">if</span> (inputToken.<span class="hljs-built_in">address</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> WSOL_MINT) {
        const maxAmount <span class="hljs-operator">=</span> Math.<span class="hljs-built_in">max</span>(
          <span class="hljs-number">0</span>,
          (balance <span class="hljs-operator">*</span> LAMPORTS_PER_SOL <span class="hljs-operator">-</span> MIN_SOL_FOR_GAS) <span class="hljs-operator">/</span> LAMPORTS_PER_SOL
        );
        <span class="hljs-keyword">return</span> parseFloat(maxAmount.toFixed(inputToken.decimals));
      }

      <span class="hljs-comment">// For other tokens, use full balance</span>
      <span class="hljs-keyword">return</span> balance;
    },
    []
  );

  <span class="hljs-keyword">return</span> {
    getQuote,
    executeSwap,
    calculateOutputAmount,
    calculateMaxInput,
    loading,
    <span class="hljs-function"><span class="hljs-keyword">error</span>,
  }</span>;
};</code></pre><div class="relative header-and-anchor"><h3 id="h-construct-useswapstorets-a-zustand-store-for-managing-swap-state">Construct <code>useSwapStore.ts</code> - a Zustand Store for Managing Swap State</h3></div><p>The <code>useSwapStore.ts</code> manages the state and actions related to the <code>useSwap.ts</code> hook, ensuring that the token states are saved across sessions, providing world-class user experience. Additionally, the <code>useSwapSelector</code> hook is provided for optimized state selection.</p><pre data-type="codeBlock" text="import { create } from &quot;zustand&quot;;
import { persist } from &quot;zustand/middleware&quot;;
import { SwapState, SwapActions } from &quot;../utils/interfaces&quot;;

// Define the initial state of the swap store
const initialState: SwapState = {
  tokenFrom: null,
  tokenTo: null,
  amountFrom: &quot;&quot;,
  amountTo: &quot;&quot;,
  slippage: 3,
  quoteResponse: null,
  isCalculating: false,
  isSwapping: false,
  isFromDialogOpen: false,
  isToDialogOpen: false,
  error: null,
};

export const useSwapStore = create<SwapState &amp; SwapActions>()(
  persist(
    (set, get) => ({
      ...initialState,

      setTokenFrom: (token) => set({ tokenFrom: token }),
      setTokenTo: (token) => set({ tokenTo: token }),
      setAmountFrom: (amount) => {
        // Always set the input amount
        set({ amountFrom: amount });

        // Reset 'to' amount if 'from' amount is invalid
        if (!amount || isNaN(parseFloat(amount))) {
          set({ amountTo: &quot;0&quot;, quoteResponse: null });
        }
      },
      setAmountTo: (amount) => set({ amountTo: amount }),
      setSlippage: (slippage) => set({ slippage }),
      setQuoteResponse: (quote) => set({ quoteResponse: quote }),
      setIsCalculating: (isCalculating) => set({ isCalculating }),
      setIsSwapping: (isSwapping) => set({ isSwapping }),
      setIsFromDialogOpen: (isOpen) => set({ isFromDialogOpen: isOpen }),
      setIsToDialogOpen: (isOpen) => set({ isToDialogOpen: isOpen }),
      setError: (error) => {
        set({ error });
        // Reset 'to' amount when there's an error to trigger recalculation
        if (error) {
          set({ amountTo: &quot;&quot;, quoteResponse: null });
        }
      },

      reset: () => {
        const { tokenFrom, tokenTo } = get();
        set({
          ...initialState,
          // Preserve selected tokens
          tokenFrom,
          tokenTo,
        });
      },

      resetAmounts: () =>
        set({
          amountFrom: &quot;&quot;,
          amountTo: &quot;&quot;,
          quoteResponse: null,
          error: null,
        }),

      swapTokens: () => {
        const { tokenFrom, tokenTo, amountFrom, amountTo } = get();
        set({
          tokenFrom: tokenTo,
          tokenTo: tokenFrom,
          amountFrom: amountTo,
          amountTo: amountFrom,
        });
      },
    }),
    {
      name: &quot;swap-storage&quot;,
      // Only persist tokens, amounts, and slippage
      partialize: (state) => ({
        tokenFrom: state.tokenFrom,
        tokenTo: state.tokenTo,
        amountFrom: state.amountFrom,
        amountTo: state.amountTo,
        slippage: state.slippage,
      }),
    }
  )
);

// Selector hook for better performance
export const useSwapSelector = <T>(selector: (state: SwapState) => T) =>
  useSwapStore(selector);
"><code><span class="hljs-keyword">import</span> { <span class="hljs-title">create</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"zustand"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">persist</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"zustand/middleware"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">SwapState</span>, <span class="hljs-title">SwapActions</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../utils/interfaces"</span>;

<span class="hljs-comment">// Define the initial state of the swap store</span>
const initialState: SwapState <span class="hljs-operator">=</span> {
  tokenFrom: null,
  tokenTo: null,
  amountFrom: <span class="hljs-string">""</span>,
  amountTo: <span class="hljs-string">""</span>,
  slippage: <span class="hljs-number">3</span>,
  quoteResponse: null,
  isCalculating: <span class="hljs-literal">false</span>,
  isSwapping: <span class="hljs-literal">false</span>,
  isFromDialogOpen: <span class="hljs-literal">false</span>,
  isToDialogOpen: <span class="hljs-literal">false</span>,
  <span class="hljs-function"><span class="hljs-keyword">error</span>: <span class="hljs-title">null</span>,
}</span>;

export const useSwapStore <span class="hljs-operator">=</span> create<span class="hljs-operator">&lt;</span>SwapState <span class="hljs-operator">&amp;</span> SwapActions<span class="hljs-operator">&gt;</span>()(
  persist(
    (set, get) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> ({
      ...initialState,

      setTokenFrom: (token) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> set({ tokenFrom: token }),
      setTokenTo: (token) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> set({ tokenTo: token }),
      setAmountFrom: (amount) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
        <span class="hljs-comment">// Always set the input amount</span>
        set({ amountFrom: amount });

        <span class="hljs-comment">// Reset 'to' amount if 'from' amount is invalid</span>
        <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>amount <span class="hljs-operator">|</span><span class="hljs-operator">|</span> isNaN(parseFloat(amount))) {
          set({ amountTo: <span class="hljs-string">"0"</span>, quoteResponse: null });
        }
      },
      setAmountTo: (amount) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> set({ amountTo: amount }),
      setSlippage: (slippage) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> set({ slippage }),
      setQuoteResponse: (quote) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> set({ quoteResponse: quote }),
      setIsCalculating: (isCalculating) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> set({ isCalculating }),
      setIsSwapping: (isSwapping) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> set({ isSwapping }),
      setIsFromDialogOpen: (isOpen) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> set({ isFromDialogOpen: isOpen }),
      setIsToDialogOpen: (isOpen) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> set({ isToDialogOpen: isOpen }),
      setError: (<span class="hljs-function"><span class="hljs-keyword">error</span>) =&gt; </span>{
        set({ <span class="hljs-function"><span class="hljs-keyword">error</span> })</span>;
        <span class="hljs-comment">// Reset 'to' amount when there's an error to trigger recalculation</span>
        <span class="hljs-keyword">if</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
          set({ amountTo: <span class="hljs-string">""</span>, quoteResponse: null });
        }
      },

      reset: () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
        const { tokenFrom, tokenTo } <span class="hljs-operator">=</span> get();
        set({
          ...initialState,
          <span class="hljs-comment">// Preserve selected tokens</span>
          tokenFrom,
          tokenTo,
        });
      },

      resetAmounts: () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span>
        set({
          amountFrom: <span class="hljs-string">""</span>,
          amountTo: <span class="hljs-string">""</span>,
          quoteResponse: null,
          <span class="hljs-function"><span class="hljs-keyword">error</span>: <span class="hljs-title">null</span>,
        }),

      <span class="hljs-title">swapTokens</span>: (<span class="hljs-params"></span>) =&gt; </span>{
        const { tokenFrom, tokenTo, amountFrom, amountTo } <span class="hljs-operator">=</span> get();
        set({
          tokenFrom: tokenTo,
          tokenTo: tokenFrom,
          amountFrom: amountTo,
          amountTo: amountFrom,
        });
      },
    }),
    {
      name: <span class="hljs-string">"swap-storage"</span>,
      <span class="hljs-comment">// Only persist tokens, amounts, and slippage</span>
      partialize: (state) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> ({
        tokenFrom: state.tokenFrom,
        tokenTo: state.tokenTo,
        amountFrom: state.amountFrom,
        amountTo: state.amountTo,
        slippage: state.slippage,
      }),
    }
  )
);

<span class="hljs-comment">// Selector hook for better performance</span>
export const useSwapSelector <span class="hljs-operator">=</span> <span class="hljs-operator">&lt;</span>T<span class="hljs-operator">&gt;</span>(selector: (state: SwapState) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> T) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span>
  useSwapStore(selector);
</code></pre><div class="relative header-and-anchor"><h3 id="h-create-useswapoperationsts-a-swap-operations-hook-to-facilitate-token-swapping">Create <code>useSwapOperations.ts</code> - a Swap Operations Hook to facilitate token swapping</h3></div><p>The <code>useSwapOperations.ts</code> is designed to manage the state and logic required for calculating token swap quote, executing swaps, and handling user interactions related to token swaps. Key functionalities includes:</p><ul><li><p><strong>Quote Calculation </strong>- uses the <code>getQuote</code> function from <code>useSwap.ts</code></p></li><li><p><strong>Swap Execution </strong>- uses the <code>executeSwap</code> function from <code>useSwap.ts</code></p></li><li><p><strong>Auto-Refresh and Debounce </strong>- auto refresh and calculate debounces via <code>useEffect</code> hooks</p></li><li><p><strong>Error Handling </strong>- provides user feedback through toast notifications for various error scenarios</p></li></ul><p><strong>P.S. If your dApp already includes toast notifications, you can replace the ones in this file with the toast notifications used in your dApp.</strong></p><pre data-type="codeBlock" text="import React, { useCallback, useEffect, useRef } from &quot;react&quot;;
import { useSwapStore, useSwapSelector } from &quot;../store/useSwapStore&quot;;
import { useSwap } from &quot;./useSwap&quot;;
import { useTokenBalance } from &quot;./useTokenBalance&quot;;
import { SquareArrowOutUpRightIcon } from &quot;lucide-react&quot;;
import { toast } from &quot;sonner&quot;;

interface SwapOperationsConfig {
  rpcUrl: string;
  referralKey?: string;
  platformFeeBps?: number;
}

export const useSwapOperations = (config: SwapOperationsConfig) => {
  // State selectors
  const tokenFrom = useSwapSelector((state) => state.tokenFrom);
  const tokenTo = useSwapSelector((state) => state.tokenTo);
  const amountFrom = useSwapSelector((state) => state.amountFrom);
  const amountTo = useSwapSelector((state) => state.amountTo);
  const slippage = useSwapSelector((state) => state.slippage);
  const isCalculating = useSwapSelector((state) => state.isCalculating);
  const isSwapping = useSwapSelector((state) => state.isSwapping);

  // Actions
  const {
    setAmountFrom,
    setAmountTo,
    setQuoteResponse,
    setIsCalculating,
    setIsSwapping,
    setError,
  } = useSwapStore();

  const { getQuote, executeSwap, calculateMaxInput } = useSwap(config);
  const { balance: fromBalance, refetch: refetchFromBalance } =
    useTokenBalance(tokenFrom);
  const { balance: toBalance, refetch: refetchToBalance } =
    useTokenBalance(tokenTo);

  const refreshTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  // Memoized quote calculation with debounce
  const calculateQuote = useCallback(
    async (amount: string) => {
      // Skip quote calculation if tokens aren't selected
      if (!tokenFrom || !tokenTo) {
        setAmountTo(&quot;0&quot;);
        setQuoteResponse(null);
        setIsCalculating(false);
        return;
      }

      // Handle empty input silently
      if (!amount) {
        setAmountTo(&quot;0&quot;);
        setQuoteResponse(null);
        setIsCalculating(false);
        return;
      }

      // Show error for invalid number after debounce
      if (isNaN(parseFloat(amount))) {
        setAmountTo(&quot;0&quot;);
        setQuoteResponse(null);
        setIsCalculating(false);
        toast.error(
          <div className=&quot;flex flex-col bg-red-100 w-full p-4 rounded-lg&quot;>
            <span className=&quot;font-semibold&quot;>
              Invalid Input: Please enter a valid number
            </span>
            <span>The amount must be a valid numerical value</span>
          </div>,
          {
            style: {
              padding: 0,
              margin: 0,
            },
          }
        );
        return;
      }

      const parsedAmount = parseFloat(amount);
      // Show error for zero or negative amount after debounce
      if (parsedAmount <= 0) {
        setAmountTo(&quot;0&quot;);
        setQuoteResponse(null);
        setIsCalculating(false);
        toast.error(
          <div className=&quot;flex flex-col bg-red-100 w-full p-4 rounded-lg&quot;>
            <span className=&quot;font-semibold&quot;>Invalid Amount</span>
            <span>Amount must be greater than 0</span>
          </div>,
          {
            style: {
              padding: 0,
              margin: 0,
            },
          }
        );
        return;
      }

      // Check balance only after we have a valid amount
      if (fromBalance !== null &amp;&amp; parsedAmount > fromBalance) {
        setAmountTo(&quot;0&quot;);
        setQuoteResponse(null);
        toast.error(
          <div className=&quot;flex flex-col bg-red-100 w-full p-4 rounded-lg&quot;>
            <span className=&quot;font-semibold&quot;>Insufficient Balance</span>
            <span>You don't have enough {tokenFrom.symbol} in your wallet</span>
          </div>,
          {
            style: {
              padding: 0,
              margin: 0,
            },
          }
        );
        return;
      }

      setIsCalculating(true);
      try {
        const quote = await getQuote(
          tokenFrom,
          tokenTo,
          parsedAmount,
          slippage * 100
        );

        if (quote) {
          setQuoteResponse(quote);
          const outputAmount = (
            parseInt(quote.otherAmountThreshold) /
            Math.pow(10, tokenTo.decimals)
          ).toString();
          setAmountTo(outputAmount);
        }
      } catch (err) {
        const errorMessage =
          err instanceof Error
            ? err.message
            : &quot;Failed to calculate output amount&quot;;
        console.error(&quot;Failed to calculate output amount:&quot;, err);
        setAmountTo(&quot;0&quot;);
        setQuoteResponse(null);
        toast.error(
          <div className=&quot;flex flex-col bg-red-100 w-full p-4 rounded-lg&quot;>
            <span className=&quot;font-semibold&quot;>Quote Calculation Failed</span>
            <span className=&quot;break-words text-wrap&quot;>{errorMessage}</span>
          </div>,
          {
            style: {
              padding: 0,
              margin: 0,
            },
          }
        );
      } finally {
        setIsCalculating(false);
      }
    },
    [
      tokenFrom,
      tokenTo,
      slippage,
      fromBalance,
      getQuote,
      setAmountTo,
      setQuoteResponse,
      setIsCalculating,
    ]
  );

  // Add this new function to handle manual refresh
  const handleRefreshQuote = useCallback(() => {
    if (!amountFrom || !tokenFrom || !tokenTo) return;

    // Clear existing timer if any
    if (refreshTimerRef.current) {
      clearTimeout(refreshTimerRef.current);
    }

    // Trigger immediate quote calculation
    calculateQuote(amountFrom);

    // Start new refresh timer
    refreshTimerRef.current = setTimeout(() => {
      handleRefreshQuote();
    }, 15000);
  }, [amountFrom, tokenFrom, tokenTo, calculateQuote]);

  // Add auto-refresh effect
  useEffect(() => {
    if (amountFrom &amp;&amp; tokenFrom &amp;&amp; tokenTo &amp;&amp; !isCalculating &amp;&amp; !isSwapping) {
      refreshTimerRef.current = setTimeout(() => {
        handleRefreshQuote();
      }, 15000);
    }

    return () => {
      if (refreshTimerRef.current) {
        clearTimeout(refreshTimerRef.current);
      }
    };
  }, [
    amountFrom,
    tokenFrom,
    tokenTo,
    isCalculating,
    isSwapping,
    handleRefreshQuote,
  ]);

  // Debounced quote calculation
  const debouncedCalculateQuote = useCallback(
    (amount: string) => {
      const debounced = setTimeout(() => calculateQuote(amount), 500);
      return () => clearTimeout(debounced);
    },
    [calculateQuote]
  );

  // Effect to handle input validation and trigger quote calculation
  useEffect(() => {
    const validateAndCalculate = () => {
      // Reset 'to' amount for empty/invalid input without showing error
      if (!amountFrom || isNaN(parseFloat(amountFrom))) {
        setAmountTo(&quot;0&quot;);
        setQuoteResponse(null);
        return;
      }

      const parsedAmount = parseFloat(amountFrom);
      // Skip calculation for zero or negative amounts without showing error
      if (parsedAmount <= 0) {
        setAmountTo(&quot;0&quot;);
        setQuoteResponse(null);
        return;
      }

      // Proceed with quote calculation if input is valid and positive
      debouncedCalculateQuote(amountFrom);
    };

    validateAndCalculate();
  }, [amountFrom, debouncedCalculateQuote, setAmountTo, setQuoteResponse]);

  // Calculate maximum input amount
  const calculateMaximumInput = useCallback(async () => {
    if (!tokenFrom || !fromBalance || fromBalance <= 0) return;

    const maxAmount = await calculateMaxInput(tokenFrom, fromBalance);
    return maxAmount.toFixed(tokenFrom.decimals);
  }, [tokenFrom, fromBalance, calculateMaxInput]);

  // Handle swap execution
  const handleSwap = useCallback(async () => {
    if (!tokenFrom || !tokenTo || !amountFrom || !amountTo) {
      toast.error(
        <div className=&quot;flex flex-col bg-red-100 w-full p-4 rounded-lg&quot;>
          <span className=&quot;font-semibold&quot;>Invalid Swap</span>
          <span>
            Please ensure you have selected tokens and entered valid amounts
          </span>
        </div>,
        {
          style: {
            padding: 0,
            margin: 0,
          },
        }
      );
      return;
    }

    setIsSwapping(true);
    const loadingToast = toast.loading(
      <div className=&quot;flex flex-col p-4&quot;>
        <span className=&quot;font-semibold&quot;>Processing Transaction</span>
        <span className=&quot;text-xs text-black/50&quot;>
          Please wait while your transaction is being processed
        </span>
      </div>,
      {
        style: {
          padding: 0,
          margin: 0,
        },
      }
    );

    try {
      const quote = await getQuote(
        tokenFrom,
        tokenTo,
        parseFloat(amountFrom),
        slippage * 100
      );

      if (!quote) {
        throw new Error(&quot;Failed to get quote&quot;);
      }

      const txid = await executeSwap(quote);
      if (!txid) {
        throw new Error(&quot;Failed to execute swap&quot;);
      }

      // Reset amounts and states only on successful swap
      setAmountFrom(&quot;&quot;);
      setAmountTo(&quot;0&quot;);
      setQuoteResponse(null);
      setError(null);

      toast.dismiss(loadingToast);
      toast.success(
        <div className=&quot;flex flex-col bg-green-100 w-full p-4  rounded-lg&quot;>
          <span className=&quot;font-semibold&quot;>Swap Successful!</span>
          <span className=&quot;text-xs text-black/50&quot;>
            <span className=&quot;break-words&quot;>Transaction ID:</span>
            <a
              href={`https://solana.fm/tx/${txid}`}
              target=&quot;_blank&quot;
              rel=&quot;noopener noreferrer&quot;
              className=&quot;text-blue-500 hover:underline break-words&quot;
            >
              {txid}{&quot; &quot;}
              <SquareArrowOutUpRightIcon className=&quot;h-3 w-3 inline align-text-bottom&quot; />
            </a>
          </span>
        </div>,
        {
          style: {
            padding: 0,
            margin: 0,
          },
        }
      );

      await Promise.all([refetchFromBalance(), refetchToBalance()]);
    } catch (err) {
      const errorMessage =
        err instanceof Error ? err.message : &quot;Failed to execute swap&quot;;

      toast.dismiss(loadingToast);

      // Check if user rejected/cancelled the transaction
      if (
        errorMessage.toLowerCase().includes(&quot;user rejected&quot;) ||
        errorMessage.toLowerCase().includes(&quot;cancelled&quot;)
      ) {
        toast.error(
          <div className=&quot;flex flex-col bg-red-100 w-full p-4 rounded-lg&quot;>
            <span className=&quot;font-semibold&quot;>Transaction Cancelled</span>
            <span>You cancelled the transaction</span>
          </div>,
          {
            style: {
              padding: 0,
              margin: 0,
            },
          }
        );
      } else {
        toast.error(
          <div className=&quot;flex flex-col bg-red-100 w-full p-4 rounded-lg&quot;>
            <span className=&quot;font-semibold&quot;>Transaction Failed</span>
            <span className=&quot;break-words text-wrap&quot;>{errorMessage}</span>
          </div>,
          {
            style: {
              padding: 0,
              margin: 0,
            },
          }
        );
      }

      console.error(&quot;Swap failed:&quot;, err);
      setError(errorMessage);

      // Refetch quote to ensure prices are up to date
      if (amountFrom &amp;&amp; !isNaN(parseFloat(amountFrom))) {
        calculateQuote(amountFrom);
      }
    } finally {
      setIsSwapping(false);
    }
  }, [
    tokenFrom,
    tokenTo,
    amountFrom,
    amountTo,
    slippage,
    getQuote,
    executeSwap,
    setAmountFrom,
    setAmountTo,
    setQuoteResponse,
    setIsCalculating,
    setIsSwapping,
    setError,
    refetchFromBalance,
    refetchToBalance,
  ]);

  return {
    calculateMaximumInput,
    handleSwap,
    handleRefreshQuote,
    fromBalance,
    toBalance,
  };
};"><code><span class="hljs-keyword">import</span> <span class="hljs-title class_">React</span>, { useCallback, useEffect, useRef } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { useSwapStore, useSwapSelector } <span class="hljs-keyword">from</span> <span class="hljs-string">"../store/useSwapStore"</span>;
<span class="hljs-keyword">import</span> { useSwap } <span class="hljs-keyword">from</span> <span class="hljs-string">"./useSwap"</span>;
<span class="hljs-keyword">import</span> { useTokenBalance } <span class="hljs-keyword">from</span> <span class="hljs-string">"./useTokenBalance"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title class_">SquareArrowOutUpRightIcon</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">"lucide-react"</span>;
<span class="hljs-keyword">import</span> { toast } <span class="hljs-keyword">from</span> <span class="hljs-string">"sonner"</span>;

<span class="hljs-keyword">interface</span> <span class="hljs-title class_">SwapOperationsConfig</span> {
  <span class="hljs-attr">rpcUrl</span>: <span class="hljs-built_in">string</span>;
  referralKey?: <span class="hljs-built_in">string</span>;
  platformFeeBps?: <span class="hljs-built_in">number</span>;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> <span class="hljs-title function_">useSwapOperations</span> = (<span class="hljs-params">config: SwapOperationsConfig</span>) =&gt; {
  <span class="hljs-comment">// State selectors</span>
  <span class="hljs-keyword">const</span> tokenFrom = <span class="hljs-title function_">useSwapSelector</span>(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> state.<span class="hljs-property">tokenFrom</span>);
  <span class="hljs-keyword">const</span> tokenTo = <span class="hljs-title function_">useSwapSelector</span>(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> state.<span class="hljs-property">tokenTo</span>);
  <span class="hljs-keyword">const</span> amountFrom = <span class="hljs-title function_">useSwapSelector</span>(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> state.<span class="hljs-property">amountFrom</span>);
  <span class="hljs-keyword">const</span> amountTo = <span class="hljs-title function_">useSwapSelector</span>(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> state.<span class="hljs-property">amountTo</span>);
  <span class="hljs-keyword">const</span> slippage = <span class="hljs-title function_">useSwapSelector</span>(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> state.<span class="hljs-property">slippage</span>);
  <span class="hljs-keyword">const</span> isCalculating = <span class="hljs-title function_">useSwapSelector</span>(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> state.<span class="hljs-property">isCalculating</span>);
  <span class="hljs-keyword">const</span> isSwapping = <span class="hljs-title function_">useSwapSelector</span>(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> state.<span class="hljs-property">isSwapping</span>);

  <span class="hljs-comment">// Actions</span>
  <span class="hljs-keyword">const</span> {
    setAmountFrom,
    setAmountTo,
    setQuoteResponse,
    setIsCalculating,
    setIsSwapping,
    setError,
  } = <span class="hljs-title function_">useSwapStore</span>();

  <span class="hljs-keyword">const</span> { getQuote, executeSwap, calculateMaxInput } = <span class="hljs-title function_">useSwap</span>(config);
  <span class="hljs-keyword">const</span> { <span class="hljs-attr">balance</span>: fromBalance, <span class="hljs-attr">refetch</span>: refetchFromBalance } =
    <span class="hljs-title function_">useTokenBalance</span>(tokenFrom);
  <span class="hljs-keyword">const</span> { <span class="hljs-attr">balance</span>: toBalance, <span class="hljs-attr">refetch</span>: refetchToBalance } =
    <span class="hljs-title function_">useTokenBalance</span>(tokenTo);

  <span class="hljs-keyword">const</span> refreshTimerRef = useRef&lt;<span class="hljs-title class_">ReturnType</span>&lt;<span class="hljs-keyword">typeof</span> <span class="hljs-built_in">setTimeout</span>&gt; | <span class="hljs-literal">null</span>&gt;(<span class="hljs-literal">null</span>);

  <span class="hljs-comment">// Memoized quote calculation with debounce</span>
  <span class="hljs-keyword">const</span> calculateQuote = <span class="hljs-title function_">useCallback</span>(
    <span class="hljs-keyword">async</span> (<span class="hljs-attr">amount</span>: <span class="hljs-built_in">string</span>) =&gt; {
      <span class="hljs-comment">// Skip quote calculation if tokens aren't selected</span>
      <span class="hljs-keyword">if</span> (!tokenFrom || !tokenTo) {
        <span class="hljs-title function_">setAmountTo</span>(<span class="hljs-string">"0"</span>);
        <span class="hljs-title function_">setQuoteResponse</span>(<span class="hljs-literal">null</span>);
        <span class="hljs-title function_">setIsCalculating</span>(<span class="hljs-literal">false</span>);
        <span class="hljs-keyword">return</span>;
      }

      <span class="hljs-comment">// Handle empty input silently</span>
      <span class="hljs-keyword">if</span> (!amount) {
        <span class="hljs-title function_">setAmountTo</span>(<span class="hljs-string">"0"</span>);
        <span class="hljs-title function_">setQuoteResponse</span>(<span class="hljs-literal">null</span>);
        <span class="hljs-title function_">setIsCalculating</span>(<span class="hljs-literal">false</span>);
        <span class="hljs-keyword">return</span>;
      }

      <span class="hljs-comment">// Show error for invalid number after debounce</span>
      <span class="hljs-keyword">if</span> (<span class="hljs-built_in">isNaN</span>(<span class="hljs-built_in">parseFloat</span>(amount))) {
        <span class="hljs-title function_">setAmountTo</span>(<span class="hljs-string">"0"</span>);
        <span class="hljs-title function_">setQuoteResponse</span>(<span class="hljs-literal">null</span>);
        <span class="hljs-title function_">setIsCalculating</span>(<span class="hljs-literal">false</span>);
        toast.<span class="hljs-title function_">error</span>(
          <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col bg-red-100 w-full p-4 rounded-lg"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-semibold"</span>&gt;</span>
              Invalid Input: Please enter a valid number
            <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>The amount must be a valid numerical value<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>,
          {
            <span class="hljs-attr">style</span>: {
              <span class="hljs-attr">padding</span>: <span class="hljs-number">0</span>,
              <span class="hljs-attr">margin</span>: <span class="hljs-number">0</span>,
            },
          }
        );
        <span class="hljs-keyword">return</span>;
      }

      <span class="hljs-keyword">const</span> parsedAmount = <span class="hljs-built_in">parseFloat</span>(amount);
      <span class="hljs-comment">// Show error for zero or negative amount after debounce</span>
      <span class="hljs-keyword">if</span> (parsedAmount &lt;= <span class="hljs-number">0</span>) {
        <span class="hljs-title function_">setAmountTo</span>(<span class="hljs-string">"0"</span>);
        <span class="hljs-title function_">setQuoteResponse</span>(<span class="hljs-literal">null</span>);
        <span class="hljs-title function_">setIsCalculating</span>(<span class="hljs-literal">false</span>);
        toast.<span class="hljs-title function_">error</span>(
          <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col bg-red-100 w-full p-4 rounded-lg"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-semibold"</span>&gt;</span>Invalid Amount<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Amount must be greater than 0<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>,
          {
            <span class="hljs-attr">style</span>: {
              <span class="hljs-attr">padding</span>: <span class="hljs-number">0</span>,
              <span class="hljs-attr">margin</span>: <span class="hljs-number">0</span>,
            },
          }
        );
        <span class="hljs-keyword">return</span>;
      }

      <span class="hljs-comment">// Check balance only after we have a valid amount</span>
      <span class="hljs-keyword">if</span> (fromBalance !== <span class="hljs-literal">null</span> &amp;&amp; parsedAmount &gt; fromBalance) {
        <span class="hljs-title function_">setAmountTo</span>(<span class="hljs-string">"0"</span>);
        <span class="hljs-title function_">setQuoteResponse</span>(<span class="hljs-literal">null</span>);
        toast.<span class="hljs-title function_">error</span>(
          <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col bg-red-100 w-full p-4 rounded-lg"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-semibold"</span>&gt;</span>Insufficient Balance<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>You don't have enough {tokenFrom.symbol} in your wallet<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>,
          {
            <span class="hljs-attr">style</span>: {
              <span class="hljs-attr">padding</span>: <span class="hljs-number">0</span>,
              <span class="hljs-attr">margin</span>: <span class="hljs-number">0</span>,
            },
          }
        );
        <span class="hljs-keyword">return</span>;
      }

      <span class="hljs-title function_">setIsCalculating</span>(<span class="hljs-literal">true</span>);
      <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> quote = <span class="hljs-keyword">await</span> <span class="hljs-title function_">getQuote</span>(
          tokenFrom,
          tokenTo,
          parsedAmount,
          slippage * <span class="hljs-number">100</span>
        );

        <span class="hljs-keyword">if</span> (quote) {
          <span class="hljs-title function_">setQuoteResponse</span>(quote);
          <span class="hljs-keyword">const</span> outputAmount = (
            <span class="hljs-built_in">parseInt</span>(quote.<span class="hljs-property">otherAmountThreshold</span>) /
            <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">pow</span>(<span class="hljs-number">10</span>, tokenTo.<span class="hljs-property">decimals</span>)
          ).<span class="hljs-title function_">toString</span>();
          <span class="hljs-title function_">setAmountTo</span>(outputAmount);
        }
      } <span class="hljs-keyword">catch</span> (err) {
        <span class="hljs-keyword">const</span> errorMessage =
          err <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Error</span>
            ? err.<span class="hljs-property">message</span>
            : <span class="hljs-string">"Failed to calculate output amount"</span>;
        <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">"Failed to calculate output amount:"</span>, err);
        <span class="hljs-title function_">setAmountTo</span>(<span class="hljs-string">"0"</span>);
        <span class="hljs-title function_">setQuoteResponse</span>(<span class="hljs-literal">null</span>);
        toast.<span class="hljs-title function_">error</span>(
          <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col bg-red-100 w-full p-4 rounded-lg"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-semibold"</span>&gt;</span>Quote Calculation Failed<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"break-words text-wrap"</span>&gt;</span>{errorMessage}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>,
          {
            <span class="hljs-attr">style</span>: {
              <span class="hljs-attr">padding</span>: <span class="hljs-number">0</span>,
              <span class="hljs-attr">margin</span>: <span class="hljs-number">0</span>,
            },
          }
        );
      } <span class="hljs-keyword">finally</span> {
        <span class="hljs-title function_">setIsCalculating</span>(<span class="hljs-literal">false</span>);
      }
    },
    [
      tokenFrom,
      tokenTo,
      slippage,
      fromBalance,
      getQuote,
      setAmountTo,
      setQuoteResponse,
      setIsCalculating,
    ]
  );

  <span class="hljs-comment">// Add this new function to handle manual refresh</span>
  <span class="hljs-keyword">const</span> handleRefreshQuote = <span class="hljs-title function_">useCallback</span>(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (!amountFrom || !tokenFrom || !tokenTo) <span class="hljs-keyword">return</span>;

    <span class="hljs-comment">// Clear existing timer if any</span>
    <span class="hljs-keyword">if</span> (refreshTimerRef.<span class="hljs-property">current</span>) {
      <span class="hljs-built_in">clearTimeout</span>(refreshTimerRef.<span class="hljs-property">current</span>);
    }

    <span class="hljs-comment">// Trigger immediate quote calculation</span>
    <span class="hljs-title function_">calculateQuote</span>(amountFrom);

    <span class="hljs-comment">// Start new refresh timer</span>
    refreshTimerRef.<span class="hljs-property">current</span> = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
      <span class="hljs-title function_">handleRefreshQuote</span>();
    }, <span class="hljs-number">15000</span>);
  }, [amountFrom, tokenFrom, tokenTo, calculateQuote]);

  <span class="hljs-comment">// Add auto-refresh effect</span>
  <span class="hljs-title function_">useEffect</span>(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (amountFrom &amp;&amp; tokenFrom &amp;&amp; tokenTo &amp;&amp; !isCalculating &amp;&amp; !isSwapping) {
      refreshTimerRef.<span class="hljs-property">current</span> = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-title function_">handleRefreshQuote</span>();
      }, <span class="hljs-number">15000</span>);
    }

    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
      <span class="hljs-keyword">if</span> (refreshTimerRef.<span class="hljs-property">current</span>) {
        <span class="hljs-built_in">clearTimeout</span>(refreshTimerRef.<span class="hljs-property">current</span>);
      }
    };
  }, [
    amountFrom,
    tokenFrom,
    tokenTo,
    isCalculating,
    isSwapping,
    handleRefreshQuote,
  ]);

  <span class="hljs-comment">// Debounced quote calculation</span>
  <span class="hljs-keyword">const</span> debouncedCalculateQuote = <span class="hljs-title function_">useCallback</span>(
    <span class="hljs-function">(<span class="hljs-params">amount: <span class="hljs-built_in">string</span></span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> debounced = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> <span class="hljs-title function_">calculateQuote</span>(amount), <span class="hljs-number">500</span>);
      <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">clearTimeout</span>(debounced);
    },
    [calculateQuote]
  );

  <span class="hljs-comment">// Effect to handle input validation and trigger quote calculation</span>
  <span class="hljs-title function_">useEffect</span>(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> <span class="hljs-title function_">validateAndCalculate</span> = (<span class="hljs-params"></span>) =&gt; {
      <span class="hljs-comment">// Reset 'to' amount for empty/invalid input without showing error</span>
      <span class="hljs-keyword">if</span> (!amountFrom || <span class="hljs-built_in">isNaN</span>(<span class="hljs-built_in">parseFloat</span>(amountFrom))) {
        <span class="hljs-title function_">setAmountTo</span>(<span class="hljs-string">"0"</span>);
        <span class="hljs-title function_">setQuoteResponse</span>(<span class="hljs-literal">null</span>);
        <span class="hljs-keyword">return</span>;
      }

      <span class="hljs-keyword">const</span> parsedAmount = <span class="hljs-built_in">parseFloat</span>(amountFrom);
      <span class="hljs-comment">// Skip calculation for zero or negative amounts without showing error</span>
      <span class="hljs-keyword">if</span> (parsedAmount &lt;= <span class="hljs-number">0</span>) {
        <span class="hljs-title function_">setAmountTo</span>(<span class="hljs-string">"0"</span>);
        <span class="hljs-title function_">setQuoteResponse</span>(<span class="hljs-literal">null</span>);
        <span class="hljs-keyword">return</span>;
      }

      <span class="hljs-comment">// Proceed with quote calculation if input is valid and positive</span>
      <span class="hljs-title function_">debouncedCalculateQuote</span>(amountFrom);
    };

    <span class="hljs-title function_">validateAndCalculate</span>();
  }, [amountFrom, debouncedCalculateQuote, setAmountTo, setQuoteResponse]);

  <span class="hljs-comment">// Calculate maximum input amount</span>
  <span class="hljs-keyword">const</span> calculateMaximumInput = <span class="hljs-title function_">useCallback</span>(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">if</span> (!tokenFrom || !fromBalance || fromBalance &lt;= <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span>;

    <span class="hljs-keyword">const</span> maxAmount = <span class="hljs-keyword">await</span> <span class="hljs-title function_">calculateMaxInput</span>(tokenFrom, fromBalance);
    <span class="hljs-keyword">return</span> maxAmount.<span class="hljs-title function_">toFixed</span>(tokenFrom.<span class="hljs-property">decimals</span>);
  }, [tokenFrom, fromBalance, calculateMaxInput]);

  <span class="hljs-comment">// Handle swap execution</span>
  <span class="hljs-keyword">const</span> handleSwap = <span class="hljs-title function_">useCallback</span>(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">if</span> (!tokenFrom || !tokenTo || !amountFrom || !amountTo) {
      toast.<span class="hljs-title function_">error</span>(
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col bg-red-100 w-full p-4 rounded-lg"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-semibold"</span>&gt;</span>Invalid Swap<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>
            Please ensure you have selected tokens and entered valid amounts
          <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>,
        {
          <span class="hljs-attr">style</span>: {
            <span class="hljs-attr">padding</span>: <span class="hljs-number">0</span>,
            <span class="hljs-attr">margin</span>: <span class="hljs-number">0</span>,
          },
        }
      );
      <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-title function_">setIsSwapping</span>(<span class="hljs-literal">true</span>);
    <span class="hljs-keyword">const</span> loadingToast = toast.<span class="hljs-title function_">loading</span>(
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col p-4"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-semibold"</span>&gt;</span>Processing Transaction<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xs text-black/50"</span>&gt;</span>
          Please wait while your transaction is being processed
        <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>,
      {
        <span class="hljs-attr">style</span>: {
          <span class="hljs-attr">padding</span>: <span class="hljs-number">0</span>,
          <span class="hljs-attr">margin</span>: <span class="hljs-number">0</span>,
        },
      }
    );

    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> quote = <span class="hljs-keyword">await</span> <span class="hljs-title function_">getQuote</span>(
        tokenFrom,
        tokenTo,
        <span class="hljs-built_in">parseFloat</span>(amountFrom),
        slippage * <span class="hljs-number">100</span>
      );

      <span class="hljs-keyword">if</span> (!quote) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">"Failed to get quote"</span>);
      }

      <span class="hljs-keyword">const</span> txid = <span class="hljs-keyword">await</span> <span class="hljs-title function_">executeSwap</span>(quote);
      <span class="hljs-keyword">if</span> (!txid) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">"Failed to execute swap"</span>);
      }

      <span class="hljs-comment">// Reset amounts and states only on successful swap</span>
      <span class="hljs-title function_">setAmountFrom</span>(<span class="hljs-string">""</span>);
      <span class="hljs-title function_">setAmountTo</span>(<span class="hljs-string">"0"</span>);
      <span class="hljs-title function_">setQuoteResponse</span>(<span class="hljs-literal">null</span>);
      <span class="hljs-title function_">setError</span>(<span class="hljs-literal">null</span>);

      toast.<span class="hljs-title function_">dismiss</span>(loadingToast);
      toast.<span class="hljs-title function_">success</span>(
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col bg-green-100 w-full p-4  rounded-lg"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-semibold"</span>&gt;</span>Swap Successful!<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xs text-black/50"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"break-words"</span>&gt;</span>Transaction ID:<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
              <span class="hljs-attr">href</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">https:</span>//<span class="hljs-attr">solana.fm</span>/<span class="hljs-attr">tx</span>/${<span class="hljs-attr">txid</span>}`}
              <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>
              <span class="hljs-attr">rel</span>=<span class="hljs-string">"noopener noreferrer"</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"text-blue-500 hover:underline break-words"</span>
            &gt;</span>
              {txid}{" "}
              <span class="hljs-tag">&lt;<span class="hljs-name">SquareArrowOutUpRightIcon</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"h-3 w-3 inline align-text-bottom"</span> /&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>,
        {
          <span class="hljs-attr">style</span>: {
            <span class="hljs-attr">padding</span>: <span class="hljs-number">0</span>,
            <span class="hljs-attr">margin</span>: <span class="hljs-number">0</span>,
          },
        }
      );

      <span class="hljs-keyword">await</span> <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">all</span>([<span class="hljs-title function_">refetchFromBalance</span>(), <span class="hljs-title function_">refetchToBalance</span>()]);
    } <span class="hljs-keyword">catch</span> (err) {
      <span class="hljs-keyword">const</span> errorMessage =
        err <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">Error</span> ? err.<span class="hljs-property">message</span> : <span class="hljs-string">"Failed to execute swap"</span>;

      toast.<span class="hljs-title function_">dismiss</span>(loadingToast);

      <span class="hljs-comment">// Check if user rejected/cancelled the transaction</span>
      <span class="hljs-keyword">if</span> (
        errorMessage.<span class="hljs-title function_">toLowerCase</span>().<span class="hljs-title function_">includes</span>(<span class="hljs-string">"user rejected"</span>) ||
        errorMessage.<span class="hljs-title function_">toLowerCase</span>().<span class="hljs-title function_">includes</span>(<span class="hljs-string">"cancelled"</span>)
      ) {
        toast.<span class="hljs-title function_">error</span>(
          <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col bg-red-100 w-full p-4 rounded-lg"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-semibold"</span>&gt;</span>Transaction Cancelled<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>You cancelled the transaction<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>,
          {
            <span class="hljs-attr">style</span>: {
              <span class="hljs-attr">padding</span>: <span class="hljs-number">0</span>,
              <span class="hljs-attr">margin</span>: <span class="hljs-number">0</span>,
            },
          }
        );
      } <span class="hljs-keyword">else</span> {
        toast.<span class="hljs-title function_">error</span>(
          <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col bg-red-100 w-full p-4 rounded-lg"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-semibold"</span>&gt;</span>Transaction Failed<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"break-words text-wrap"</span>&gt;</span>{errorMessage}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>,
          {
            <span class="hljs-attr">style</span>: {
              <span class="hljs-attr">padding</span>: <span class="hljs-number">0</span>,
              <span class="hljs-attr">margin</span>: <span class="hljs-number">0</span>,
            },
          }
        );
      }

      <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">"Swap failed:"</span>, err);
      <span class="hljs-title function_">setError</span>(errorMessage);

      <span class="hljs-comment">// Refetch quote to ensure prices are up to date</span>
      <span class="hljs-keyword">if</span> (amountFrom &amp;&amp; !<span class="hljs-built_in">isNaN</span>(<span class="hljs-built_in">parseFloat</span>(amountFrom))) {
        <span class="hljs-title function_">calculateQuote</span>(amountFrom);
      }
    } <span class="hljs-keyword">finally</span> {
      <span class="hljs-title function_">setIsSwapping</span>(<span class="hljs-literal">false</span>);
    }
  }, [
    tokenFrom,
    tokenTo,
    amountFrom,
    amountTo,
    slippage,
    getQuote,
    executeSwap,
    setAmountFrom,
    setAmountTo,
    setQuoteResponse,
    setIsCalculating,
    setIsSwapping,
    setError,
    refetchFromBalance,
    refetchToBalance,
  ]);

  <span class="hljs-keyword">return</span> {
    calculateMaximumInput,
    handleSwap,
    handleRefreshQuote,
    fromBalance,
    toBalance,
  };
};</code></pre><div class="relative header-and-anchor"><h3 id="h-implementing-usetokensts-useswapts-useswapstorets-useswapoperationsts-in-your-dapp-ui">Implementing <code>useTokens.ts</code> + <code>useSwap.ts</code> + <code>useSwapStore.ts</code> + <code>useSwapOperations.ts</code> in your dApp UI</h3></div><p>With the logic already set up, we can now proceed to implement the <code>&lt;Swap /&gt;</code> component in the dApp’s UI. Essentially, since <code>useSwap.ts</code> is called from <code>useSwapOperations.ts</code>, your <code>&lt;Swap /&gt;</code> component will only need to import the following modules:</p><pre data-type="codeBlock" text="import { useTokens } from &quot;../hooks/useTokens&quot;;
import { useSwapOperations } from &quot;../hooks/useSwapOperations&quot;;
import { useSwapStore } from &quot;../store/useSwapStore&quot;;"><code><span class="hljs-keyword">import</span> { useTokens } <span class="hljs-keyword">from</span> <span class="hljs-string">"../hooks/useTokens"</span>;
<span class="hljs-keyword">import</span> { useSwapOperations } <span class="hljs-keyword">from</span> <span class="hljs-string">"../hooks/useSwapOperations"</span>;
<span class="hljs-keyword">import</span> { useSwapStore } <span class="hljs-keyword">from</span> <span class="hljs-string">"../store/useSwapStore"</span>;</code></pre><p>Thereafter, we can initialize the hooks like this:</p><pre data-type="codeBlock" text="const { tokens } = useTokens();
const {
    tokenFrom,
    tokenTo,
    amountFrom,
    amountTo,
    slippage,
    isFromDialogOpen,
    isToDialogOpen,
    isCalculating,
    isSwapping,
    setTokenFrom,
    setTokenTo,
    setAmountFrom,
    setSlippage,
    setIsFromDialogOpen,
    setIsToDialogOpen,
    swapTokens,
  } = useSwapStore();
const {
    calculateMaximumInput,
    handleSwap,
    handleRefreshQuote,
    fromBalance,
    toBalance,
  } = useSwapOperations({ rpcUrl, referralKey, platformFeeBps });"><code>const { tokens } = <span class="hljs-built_in">useTokens</span>();
const {
    tokenFrom,
    tokenTo,
    amountFrom,
    amountTo,
    slippage,
    isFromDialogOpen,
    isToDialogOpen,
    isCalculating,
    isSwapping,
    setTokenFrom,
    setTokenTo,
    setAmountFrom,
    setSlippage,
    setIsFromDialogOpen,
    setIsToDialogOpen,
    swapTokens,
  } = <span class="hljs-built_in">useSwapStore</span>();
const {
    calculateMaximumInput,
    handleSwap,
    handleRefreshQuote,
    fromBalance,
    toBalance,
  } = <span class="hljs-built_in">useSwapOperations</span>({ rpcUrl, referralKey, platformFeeBps });</code></pre><p>After initializing the hooks, you can now use these variables and methods to implement the core functionality and UI interactions of the <code>&lt;Swap /&gt;</code> component that you desire.</p><p>Here’s a TL;DR example of how I integrated the variables and methods into my <code>&lt;Swap /&gt;</code> component. Note that I modularized my codebase by creating custom components and helper functions to streamline the swap interface. The implementation utilizes <strong>NextJS</strong> (TypeScript) and <strong>TailwindCSS</strong> for a clean and efficient build.</p><pre data-type="codeBlock" text="import React, { useEffect } from &quot;react&quot;;
import { ReactComponent as JupiterBrightLogo } from &quot;../../assets/powered-by-jupiter/poweredbyjupiter-bright.svg&quot;;
import { ReactComponent as JupiterDarkLogo } from &quot;../../assets/powered-by-jupiter/poweredbyjupiter-dark.svg&quot;;
import { ArrowDown, Loader2, WalletIcon } from &quot;lucide-react&quot;;
import { useWallet } from &quot;@solana/wallet-adapter-react&quot;;
import { UnifiedWalletButton } from &quot;@jup-ag/wallet-adapter&quot;;
import SwapReset from &quot;./SwapReset&quot;;
import SwapSettings from &quot;./SwapSettings&quot;;
import SwapTokenButton from &quot;./SwapTokenButton&quot;;
import SwapTokenDialog from &quot;./SwapTokenDialog&quot;;
import { useTokens } from &quot;../hooks/useTokens&quot;;
import { useSwapOperations } from &quot;../hooks/useSwapOperations&quot;;
import { useSwapStore } from &quot;../store/useSwapStore&quot;;
import { formatBalance } from &quot;../helpers/formatBalance&quot;;

interface SwapProps {
  rpcUrl: string;
  referralKey?: string;
  platformFeeBps?: number;
}

const Swap = ({ rpcUrl, referralKey, platformFeeBps = 0 }: SwapProps) => {
  const { tokens } = useTokens();
  const { connected } = useWallet();
  const {
    tokenFrom,
    tokenTo,
    amountFrom,
    amountTo,
    slippage,
    isFromDialogOpen,
    isToDialogOpen,
    isCalculating,
    isSwapping,
    setTokenFrom,
    setTokenTo,
    setAmountFrom,
    setSlippage,
    setIsFromDialogOpen,
    setIsToDialogOpen,
    swapTokens,
  } = useSwapStore();

  const {
    calculateMaximumInput,
    handleSwap,
    handleRefreshQuote,
    fromBalance,
    toBalance,
  } = useSwapOperations({ rpcUrl, referralKey, platformFeeBps });

  // Initialize default tokens only if no tokens are selected or persisted
  useEffect(() => {
    // Skip if tokens haven't loaded yet
    if (tokens.length === 0) return;

    // Skip if we already have tokens selected
    if (tokenFrom || tokenTo) return;

    // Check if we have persisted tokens in localStorage
    const persistedState = localStorage.getItem(&quot;swap-storage&quot;);
    if (persistedState) {
      const { state } = JSON.parse(persistedState);
      // Skip if we have persisted tokens
      if (state.tokenFrom || state.tokenTo) return;
    }

    // Set default tokens only if no tokens are selected or persisted
    const solToken = tokens.find((token) => token.symbol === &quot;SOL&quot;);
    const usdcToken = tokens.find((token) => token.symbol === &quot;USDC&quot;);

    if (solToken &amp;&amp; usdcToken) {
      setTokenFrom(solToken);
      setTokenTo(usdcToken);
    }
  }, [tokens, tokenFrom, tokenTo]); // Run when tokens load or selection changes

  const isSwapDisabled =
    !connected ||
    !tokenFrom ||
    !tokenTo ||
    !amountFrom ||
    !amountTo ||
    isCalculating ||
    isSwapping;

  return (
    <>
      <div className=&quot;w-full max-w-[480px] p-4 font-sans&quot;>
        <div className=&quot;rounded-3xl shadow-xl bg-background dark:bg-background-dark&quot;>
          <div className=&quot;p-6&quot;>
            {/* Header */}
            <div className=&quot;flex justify-between items-center mb-6&quot;>
              <h2 className=&quot;text-xl font-bold text-foreground dark:text-foreground-dark&quot;>
                Swap
              </h2>
              <div className=&quot;flex items-center&quot;>
                <SwapSettings
                  slippage={slippage}
                  onSlippageChange={setSlippage}
                  onSave={() => {}}
                />
                <SwapReset
                  onRefresh={handleRefreshQuote}
                  isCalculating={isCalculating}
                  isSwapping={isSwapping}
                />
              </div>
            </div>

            {/* From Token Input */}
            <div className=&quot;themed-card rounded-2xl p-4 mb-2&quot;>
              <div className=&quot;flex justify-between mb-3&quot;>
                <input
                  type=&quot;number&quot;
                  placeholder=&quot;0&quot;
                  value={amountFrom}
                  onChange={(e) => setAmountFrom(e.target.value)}
                  className=&quot;w-full bg-transparent text-xl font-medium p-0 h-auto focus-visible:outline-none focus-visible:ring-0 [appearance:textfield] [&amp;::-webkit-outer-spin-button]:appearance-none [&amp;::-webkit-inner-spin-button]:appearance-none&quot;
                  disabled={isSwapping}
                />
                <SwapTokenButton
                  token={tokenFrom}
                  onClick={() => setIsFromDialogOpen(true)}
                />
              </div>
              <div className=&quot;flex justify-between items-center&quot;>
                <div className=&quot;text-sm text-muted-foreground dark:text-muted-dark-foreground&quot;>
                  {tokenFrom &amp;&amp;
                    `Balance: ${formatBalance(fromBalance)} ${
                      tokenFrom.symbol
                    }`}
                </div>
                <div className=&quot;flex gap-1.5&quot;>
                  <button
                    className=&quot;h-7 px-2.5 text-xs font-medium rounded-lg transition-colors hover:bg-accent hover:text-accent-foreground&quot;
                    onClick={async () => {
                      if (fromBalance &amp;&amp; tokenFrom) {
                        const amount = (fromBalance * 0.25).toFixed(
                          tokenFrom.decimals
                        );
                        setAmountFrom(amount);
                      }
                    }}
                    disabled={!fromBalance || fromBalance <= 0}
                  >
                    25%
                  </button>
                  <button
                    className=&quot;h-7 px-2.5 text-xs font-medium rounded-lg transition-colors hover:bg-accent hover:text-accent-foreground&quot;
                    onClick={async () => {
                      if (fromBalance &amp;&amp; tokenFrom) {
                        const amount = (fromBalance * 0.5).toFixed(
                          tokenFrom.decimals
                        );
                        setAmountFrom(amount);
                      }
                    }}
                    disabled={!fromBalance || fromBalance <= 0}
                  >
                    50%
                  </button>
                  <button
                    className=&quot;h-7 px-2.5 text-xs font-medium rounded-lg transition-colors hover:bg-accent hover:text-accent-foreground&quot;
                    onClick={async () => {
                      const maxAmount = await calculateMaximumInput();
                      if (maxAmount) setAmountFrom(maxAmount);
                    }}
                    disabled={!fromBalance || fromBalance <= 0}
                  >
                    MAX
                  </button>
                </div>
              </div>
            </div>

            {/* Swap Button */}
            <div className=&quot;flex justify-center -my-3 relative z-10 mt-3 mb-3&quot;>
              <button
                className=&quot;h-9 w-9 cursor-pointer rounded-xl bg-card dark:bg-card-dark hover:bg-accent dark:hover:bg-accent transition-colors flex items-center justify-center shadow-md disabled:opacity-50&quot;
                onClick={swapTokens}
                disabled={isSwapping}
                aria-label=&quot;Swap token positions&quot;
              >
                <ArrowDown className=&quot;h-4 w-4 text-foreground dark:text-foreground-dark&quot; />
              </button>
            </div>

            {/* To Token Input */}
            <div className=&quot;themed-card rounded-2xl p-4 mt-2 bg-muted/20 dark:bg-muted-dark/20&quot;>
              <div className=&quot;flex justify-between mb-3&quot;>
                <input
                  type=&quot;number&quot;
                  placeholder=&quot;0&quot;
                  value={amountTo}
                  readOnly
                  className=&quot;w-full bg-transparent text-xl font-medium p-0 h-auto focus-visible:outline-none focus-visible:ring-0 [appearance:textfield] [&amp;::-webkit-outer-spin-button]:appearance-none [&amp;::-webkit-inner-spin-button]:appearance-none&quot;
                />
                <SwapTokenButton
                  token={tokenTo}
                  onClick={() => setIsToDialogOpen(true)}
                />
              </div>
              <div className=&quot;text-sm text-muted-foreground dark:text-muted-dark-foreground&quot;>
                {tokenTo &amp;&amp;
                  `Balance: ${formatBalance(toBalance)} ${tokenTo.symbol}`}
              </div>
            </div>

            {/* Swap Button or Connect Wallet Button */}
            {connected ? (
              <button
                className=&quot;w-full h-10 mt-6 py-4 text-base font-semibold rounded-2xl themed-button-primary disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center&quot;
                onClick={handleSwap}
                disabled={isSwapDisabled}
              >
                {isCalculating ? (
                  <>
                    <Loader2 className=&quot;mr-2 h-5 w-5 animate-spin&quot; />
                    Loading...
                  </>
                ) : isSwapping ? (
                  <>
                    <Loader2 className=&quot;mr-2 h-5 w-5 animate-spin&quot; />
                    Swapping...
                  </>
                ) : (
                  &quot;Swap&quot;
                )}
              </button>
            ) : (
              <UnifiedWalletButton
                buttonClassName=&quot;font-sans font-semibold rounded-3xl mt-6 themed-button-primary w-full px-4 py-3 text-sm flex justify-center items-center text-center gap-2 hover:opacity-90 dark:hover:opacity-90 transition-colors text-white dark:text-white cursor-pointer&quot;
                overrideContent={
                  <>
                    <WalletIcon className=&quot;h-4 w-4 opacity-70&quot; />
                    Connect Wallet
                  </>
                }
              />
            )}

            {/* Powered by Jupiter logo */}
            <div className=&quot;flex justify-center mt-6&quot;>
              <a
                href=&quot;https://jup.ag&quot;
                target=&quot;_blank&quot;
                rel=&quot;noopener noreferrer&quot;
              >
                <JupiterBrightLogo
                  width={150}
                  height={25}
                  className=&quot;block dark:hidden opacity-75 hover:opacity-100 transition-opacity&quot;
                  preserveAspectRatio=&quot;xMidYMid meet&quot;
                />
                <JupiterDarkLogo
                  width={150}
                  height={25}
                  className=&quot;hidden dark:block opacity-75 hover:opacity-100 transition-opacity&quot;
                  preserveAspectRatio=&quot;xMidYMid meet&quot;
                />
              </a>
            </div>
          </div>
        </div>

        {/* Token Selection Dialogs */}
        <SwapTokenDialog
          open={isFromDialogOpen}
          onClose={() => setIsFromDialogOpen(false)}
          onSelect={setTokenFrom}
        />
        <SwapTokenDialog
          open={isToDialogOpen}
          onClose={() => setIsToDialogOpen(false)}
          onSelect={setTokenTo}
        />
      </div>
    </>
  );
};

export default Swap;"><code><span class="hljs-keyword">import</span> <span class="hljs-title">React</span>, { <span class="hljs-title">useEffect</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ReactComponent</span> <span class="hljs-title"><span class="hljs-keyword">as</span></span> <span class="hljs-title">JupiterBrightLogo</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../../assets/powered-by-jupiter/poweredbyjupiter-bright.svg"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ReactComponent</span> <span class="hljs-title"><span class="hljs-keyword">as</span></span> <span class="hljs-title">JupiterDarkLogo</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../../assets/powered-by-jupiter/poweredbyjupiter-dark.svg"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ArrowDown</span>, <span class="hljs-title">Loader2</span>, <span class="hljs-title">WalletIcon</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"lucide-react"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">useWallet</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@solana/wallet-adapter-react"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">UnifiedWalletButton</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@jup-ag/wallet-adapter"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">SwapReset</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./SwapReset"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">SwapSettings</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./SwapSettings"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">SwapTokenButton</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./SwapTokenButton"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">SwapTokenDialog</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./SwapTokenDialog"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">useTokens</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../hooks/useTokens"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">useSwapOperations</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../hooks/useSwapOperations"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">useSwapStore</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../store/useSwapStore"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">formatBalance</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../helpers/formatBalance"</span>;

<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">SwapProps</span> </span>{
  rpcUrl: <span class="hljs-keyword">string</span>;
  referralKey?: <span class="hljs-keyword">string</span>;
  platformFeeBps?: number;
}

const Swap <span class="hljs-operator">=</span> ({ rpcUrl, referralKey, platformFeeBps <span class="hljs-operator">=</span> <span class="hljs-number">0</span> }: SwapProps) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  const { tokens } <span class="hljs-operator">=</span> useTokens();
  const { connected } <span class="hljs-operator">=</span> useWallet();
  const {
    tokenFrom,
    tokenTo,
    amountFrom,
    amountTo,
    slippage,
    isFromDialogOpen,
    isToDialogOpen,
    isCalculating,
    isSwapping,
    setTokenFrom,
    setTokenTo,
    setAmountFrom,
    setSlippage,
    setIsFromDialogOpen,
    setIsToDialogOpen,
    swapTokens,
  } <span class="hljs-operator">=</span> useSwapStore();

  const {
    calculateMaximumInput,
    handleSwap,
    handleRefreshQuote,
    fromBalance,
    toBalance,
  } <span class="hljs-operator">=</span> useSwapOperations({ rpcUrl, referralKey, platformFeeBps });

  <span class="hljs-comment">// Initialize default tokens only if no tokens are selected or persisted</span>
  useEffect(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    <span class="hljs-comment">// Skip if tokens haven't loaded yet</span>
    <span class="hljs-keyword">if</span> (tokens.<span class="hljs-built_in">length</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span>;

    <span class="hljs-comment">// Skip if we already have tokens selected</span>
    <span class="hljs-keyword">if</span> (tokenFrom <span class="hljs-operator">|</span><span class="hljs-operator">|</span> tokenTo) <span class="hljs-keyword">return</span>;

    <span class="hljs-comment">// Check if we have persisted tokens in localStorage</span>
    const persistedState <span class="hljs-operator">=</span> localStorage.getItem(<span class="hljs-string">"swap-storage"</span>);
    <span class="hljs-keyword">if</span> (persistedState) {
      const { state } <span class="hljs-operator">=</span> JSON.parse(persistedState);
      <span class="hljs-comment">// Skip if we have persisted tokens</span>
      <span class="hljs-keyword">if</span> (state.tokenFrom <span class="hljs-operator">|</span><span class="hljs-operator">|</span> state.tokenTo) <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-comment">// Set default tokens only if no tokens are selected or persisted</span>
    const solToken <span class="hljs-operator">=</span> tokens.find((token) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> token.symbol <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"SOL"</span>);
    const usdcToken <span class="hljs-operator">=</span> tokens.find((token) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> token.symbol <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"USDC"</span>);

    <span class="hljs-keyword">if</span> (solToken <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> usdcToken) {
      setTokenFrom(solToken);
      setTokenTo(usdcToken);
    }
  }, [tokens, tokenFrom, tokenTo]); <span class="hljs-comment">// Run when tokens load or selection changes</span>

  const isSwapDisabled <span class="hljs-operator">=</span>
    <span class="hljs-operator">!</span>connected <span class="hljs-operator">|</span><span class="hljs-operator">|</span>
    <span class="hljs-operator">!</span>tokenFrom <span class="hljs-operator">|</span><span class="hljs-operator">|</span>
    <span class="hljs-operator">!</span>tokenTo <span class="hljs-operator">|</span><span class="hljs-operator">|</span>
    <span class="hljs-operator">!</span>amountFrom <span class="hljs-operator">|</span><span class="hljs-operator">|</span>
    <span class="hljs-operator">!</span>amountTo <span class="hljs-operator">|</span><span class="hljs-operator">|</span>
    isCalculating <span class="hljs-operator">|</span><span class="hljs-operator">|</span>
    isSwapping;

  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"w-full max-w-[480px] p-4 font-sans"</span><span class="hljs-operator">&gt;</span>
        <span class="hljs-operator">&lt;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"rounded-3xl shadow-xl bg-background dark:bg-background-dark"</span><span class="hljs-operator">&gt;</span>
          <span class="hljs-operator">&lt;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"p-6"</span><span class="hljs-operator">&gt;</span>
            {<span class="hljs-comment">/* Header */</span>}
            <span class="hljs-operator">&lt;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"flex justify-between items-center mb-6"</span><span class="hljs-operator">&gt;</span>
              <span class="hljs-operator">&lt;</span>h2 className<span class="hljs-operator">=</span><span class="hljs-string">"text-xl font-bold text-foreground dark:text-foreground-dark"</span><span class="hljs-operator">&gt;</span>
                Swap
              <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>h2<span class="hljs-operator">&gt;</span>
              <span class="hljs-operator">&lt;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"flex items-center"</span><span class="hljs-operator">&gt;</span>
                <span class="hljs-operator">&lt;</span>SwapSettings
                  slippage<span class="hljs-operator">=</span>{slippage}
                  onSlippageChange<span class="hljs-operator">=</span>{setSlippage}
                  onSave<span class="hljs-operator">=</span>{() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {}}
                <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
                <span class="hljs-operator">&lt;</span>SwapReset
                  onRefresh<span class="hljs-operator">=</span>{handleRefreshQuote}
                  isCalculating<span class="hljs-operator">=</span>{isCalculating}
                  isSwapping<span class="hljs-operator">=</span>{isSwapping}
                <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
              <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
            <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>

            {<span class="hljs-comment">/* From Token Input */</span>}
            <span class="hljs-operator">&lt;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"themed-card rounded-2xl p-4 mb-2"</span><span class="hljs-operator">&gt;</span>
              <span class="hljs-operator">&lt;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"flex justify-between mb-3"</span><span class="hljs-operator">&gt;</span>
                <span class="hljs-operator">&lt;</span>input
                  <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"number"</span>
                  placeholder<span class="hljs-operator">=</span><span class="hljs-string">"0"</span>
                  value<span class="hljs-operator">=</span>{amountFrom}
                  onChange<span class="hljs-operator">=</span>{(e) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> setAmountFrom(e.target.<span class="hljs-built_in">value</span>)}
                  className<span class="hljs-operator">=</span><span class="hljs-string">"w-full bg-transparent text-xl font-medium p-0 h-auto focus-visible:outline-none focus-visible:ring-0 [appearance:textfield] [&amp;::-webkit-outer-spin-button]:appearance-none [&amp;::-webkit-inner-spin-button]:appearance-none"</span>
                  disabled<span class="hljs-operator">=</span>{isSwapping}
                <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
                <span class="hljs-operator">&lt;</span>SwapTokenButton
                  token<span class="hljs-operator">=</span>{tokenFrom}
                  onClick<span class="hljs-operator">=</span>{() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> setIsFromDialogOpen(<span class="hljs-literal">true</span>)}
                <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
              <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
              <span class="hljs-operator">&lt;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"flex justify-between items-center"</span><span class="hljs-operator">&gt;</span>
                <span class="hljs-operator">&lt;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"text-sm text-muted-foreground dark:text-muted-dark-foreground"</span><span class="hljs-operator">&gt;</span>
                  {tokenFrom <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span>
                    `Balance: ${formatBalance(fromBalance)} ${
                      tokenFrom.symbol
                    }`}
                <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
                <span class="hljs-operator">&lt;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"flex gap-1.5"</span><span class="hljs-operator">&gt;</span>
                  <span class="hljs-operator">&lt;</span>button
                    className<span class="hljs-operator">=</span><span class="hljs-string">"h-7 px-2.5 text-xs font-medium rounded-lg transition-colors hover:bg-accent hover:text-accent-foreground"</span>
                    onClick<span class="hljs-operator">=</span>{async () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                      <span class="hljs-keyword">if</span> (fromBalance <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> tokenFrom) {
                        const amount <span class="hljs-operator">=</span> (fromBalance <span class="hljs-operator">*</span> <span class="hljs-number">0</span><span class="hljs-number">.25</span>).toFixed(
                          tokenFrom.decimals
                        );
                        setAmountFrom(amount);
                      }
                    }}
                    disabled<span class="hljs-operator">=</span>{<span class="hljs-operator">!</span>fromBalance <span class="hljs-operator">|</span><span class="hljs-operator">|</span> fromBalance <span class="hljs-operator">&lt;</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>}
                  <span class="hljs-operator">&gt;</span>
                    <span class="hljs-number">25</span><span class="hljs-operator">%</span>
                  <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>button<span class="hljs-operator">&gt;</span>
                  <span class="hljs-operator">&lt;</span>button
                    className<span class="hljs-operator">=</span><span class="hljs-string">"h-7 px-2.5 text-xs font-medium rounded-lg transition-colors hover:bg-accent hover:text-accent-foreground"</span>
                    onClick<span class="hljs-operator">=</span>{async () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                      <span class="hljs-keyword">if</span> (fromBalance <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> tokenFrom) {
                        const amount <span class="hljs-operator">=</span> (fromBalance <span class="hljs-operator">*</span> <span class="hljs-number">0</span><span class="hljs-number">.5</span>).toFixed(
                          tokenFrom.decimals
                        );
                        setAmountFrom(amount);
                      }
                    }}
                    disabled<span class="hljs-operator">=</span>{<span class="hljs-operator">!</span>fromBalance <span class="hljs-operator">|</span><span class="hljs-operator">|</span> fromBalance <span class="hljs-operator">&lt;</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>}
                  <span class="hljs-operator">&gt;</span>
                    <span class="hljs-number">50</span><span class="hljs-operator">%</span>
                  <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>button<span class="hljs-operator">&gt;</span>
                  <span class="hljs-operator">&lt;</span>button
                    className<span class="hljs-operator">=</span><span class="hljs-string">"h-7 px-2.5 text-xs font-medium rounded-lg transition-colors hover:bg-accent hover:text-accent-foreground"</span>
                    onClick<span class="hljs-operator">=</span>{async () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
                      const maxAmount <span class="hljs-operator">=</span> await calculateMaximumInput();
                      <span class="hljs-keyword">if</span> (maxAmount) setAmountFrom(maxAmount);
                    }}
                    disabled<span class="hljs-operator">=</span>{<span class="hljs-operator">!</span>fromBalance <span class="hljs-operator">|</span><span class="hljs-operator">|</span> fromBalance <span class="hljs-operator">&lt;</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>}
                  <span class="hljs-operator">&gt;</span>
                    MAX
                  <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>button<span class="hljs-operator">&gt;</span>
                <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
              <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
            <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>

            {<span class="hljs-comment">/* Swap Button */</span>}
            <span class="hljs-operator">&lt;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"flex justify-center -my-3 relative z-10 mt-3 mb-3"</span><span class="hljs-operator">&gt;</span>
              <span class="hljs-operator">&lt;</span>button
                className<span class="hljs-operator">=</span><span class="hljs-string">"h-9 w-9 cursor-pointer rounded-xl bg-card dark:bg-card-dark hover:bg-accent dark:hover:bg-accent transition-colors flex items-center justify-center shadow-md disabled:opacity-50"</span>
                onClick<span class="hljs-operator">=</span>{swapTokens}
                disabled<span class="hljs-operator">=</span>{isSwapping}
                aria<span class="hljs-operator">-</span>label<span class="hljs-operator">=</span><span class="hljs-string">"Swap token positions"</span>
              <span class="hljs-operator">&gt;</span>
                <span class="hljs-operator">&lt;</span>ArrowDown className<span class="hljs-operator">=</span><span class="hljs-string">"h-4 w-4 text-foreground dark:text-foreground-dark"</span> <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
              <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>button<span class="hljs-operator">&gt;</span>
            <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>

            {<span class="hljs-comment">/* To Token Input */</span>}
            <span class="hljs-operator">&lt;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"themed-card rounded-2xl p-4 mt-2 bg-muted/20 dark:bg-muted-dark/20"</span><span class="hljs-operator">&gt;</span>
              <span class="hljs-operator">&lt;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"flex justify-between mb-3"</span><span class="hljs-operator">&gt;</span>
                <span class="hljs-operator">&lt;</span>input
                  <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"number"</span>
                  placeholder<span class="hljs-operator">=</span><span class="hljs-string">"0"</span>
                  value<span class="hljs-operator">=</span>{amountTo}
                  readOnly
                  className<span class="hljs-operator">=</span><span class="hljs-string">"w-full bg-transparent text-xl font-medium p-0 h-auto focus-visible:outline-none focus-visible:ring-0 [appearance:textfield] [&amp;::-webkit-outer-spin-button]:appearance-none [&amp;::-webkit-inner-spin-button]:appearance-none"</span>
                <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
                <span class="hljs-operator">&lt;</span>SwapTokenButton
                  token<span class="hljs-operator">=</span>{tokenTo}
                  onClick<span class="hljs-operator">=</span>{() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> setIsToDialogOpen(<span class="hljs-literal">true</span>)}
                <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
              <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
              <span class="hljs-operator">&lt;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"text-sm text-muted-foreground dark:text-muted-dark-foreground"</span><span class="hljs-operator">&gt;</span>
                {tokenTo <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span>
                  `Balance: ${formatBalance(toBalance)} ${tokenTo.symbol}`}
              <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
            <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>

            {<span class="hljs-comment">/* Swap Button or Connect Wallet Button */</span>}
            {connected ? (
              <span class="hljs-operator">&lt;</span>button
                className<span class="hljs-operator">=</span><span class="hljs-string">"w-full h-10 mt-6 py-4 text-base font-semibold rounded-2xl themed-button-primary disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center"</span>
                onClick<span class="hljs-operator">=</span>{handleSwap}
                disabled<span class="hljs-operator">=</span>{isSwapDisabled}
              <span class="hljs-operator">&gt;</span>
                {isCalculating ? (
                  <span class="hljs-operator">&lt;</span><span class="hljs-operator">&gt;</span>
                    <span class="hljs-operator">&lt;</span>Loader2 className<span class="hljs-operator">=</span><span class="hljs-string">"mr-2 h-5 w-5 animate-spin"</span> <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
                    Loading...
                  <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
                ) : isSwapping ? (
                  <span class="hljs-operator">&lt;</span><span class="hljs-operator">&gt;</span>
                    <span class="hljs-operator">&lt;</span>Loader2 className<span class="hljs-operator">=</span><span class="hljs-string">"mr-2 h-5 w-5 animate-spin"</span> <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
                    Swapping...
                  <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
                ) : (
                  <span class="hljs-string">"Swap"</span>
                )}
              <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>button<span class="hljs-operator">&gt;</span>
            ) : (
              <span class="hljs-operator">&lt;</span>UnifiedWalletButton
                buttonClassName<span class="hljs-operator">=</span><span class="hljs-string">"font-sans font-semibold rounded-3xl mt-6 themed-button-primary w-full px-4 py-3 text-sm flex justify-center items-center text-center gap-2 hover:opacity-90 dark:hover:opacity-90 transition-colors text-white dark:text-white cursor-pointer"</span>
                overrideContent<span class="hljs-operator">=</span>{
                  <span class="hljs-operator">&lt;</span><span class="hljs-operator">&gt;</span>
                    <span class="hljs-operator">&lt;</span>WalletIcon className<span class="hljs-operator">=</span><span class="hljs-string">"h-4 w-4 opacity-70"</span> <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
                    Connect Wallet
                  <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
                }
              <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
            )}

            {<span class="hljs-comment">/* Powered by Jupiter logo */</span>}
            <span class="hljs-operator">&lt;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"flex justify-center mt-6"</span><span class="hljs-operator">&gt;</span>
              <span class="hljs-operator">&lt;</span>a
                href<span class="hljs-operator">=</span><span class="hljs-string">"https://jup.ag"</span>
                target<span class="hljs-operator">=</span><span class="hljs-string">"_blank"</span>
                rel<span class="hljs-operator">=</span><span class="hljs-string">"noopener noreferrer"</span>
              <span class="hljs-operator">&gt;</span>
                <span class="hljs-operator">&lt;</span>JupiterBrightLogo
                  width<span class="hljs-operator">=</span>{<span class="hljs-number">150</span>}
                  height<span class="hljs-operator">=</span>{<span class="hljs-number">25</span>}
                  className<span class="hljs-operator">=</span><span class="hljs-string">"block dark:hidden opacity-75 hover:opacity-100 transition-opacity"</span>
                  preserveAspectRatio<span class="hljs-operator">=</span><span class="hljs-string">"xMidYMid meet"</span>
                <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
                <span class="hljs-operator">&lt;</span>JupiterDarkLogo
                  width<span class="hljs-operator">=</span>{<span class="hljs-number">150</span>}
                  height<span class="hljs-operator">=</span>{<span class="hljs-number">25</span>}
                  className<span class="hljs-operator">=</span><span class="hljs-string">"hidden dark:block opacity-75 hover:opacity-100 transition-opacity"</span>
                  preserveAspectRatio<span class="hljs-operator">=</span><span class="hljs-string">"xMidYMid meet"</span>
                <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
              <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>a<span class="hljs-operator">&gt;</span>
            <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
          <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
        <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>

        {<span class="hljs-comment">/* Token Selection Dialogs */</span>}
        <span class="hljs-operator">&lt;</span>SwapTokenDialog
          open<span class="hljs-operator">=</span>{isFromDialogOpen}
          onClose<span class="hljs-operator">=</span>{() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> setIsFromDialogOpen(<span class="hljs-literal">false</span>)}
          onSelect<span class="hljs-operator">=</span>{setTokenFrom}
        <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
        <span class="hljs-operator">&lt;</span>SwapTokenDialog
          open<span class="hljs-operator">=</span>{isToDialogOpen}
          onClose<span class="hljs-operator">=</span>{() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> setIsToDialogOpen(<span class="hljs-literal">false</span>)}
          onSelect<span class="hljs-operator">=</span>{setTokenTo}
        <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
  );
};

export default Swap;</code></pre><p>That's all it takes to implement the logic for the &lt;Swap /&gt; component in your dApp. </p><p>Straightforward, isn't it? <span data-name="stuck_out_tongue_winking_eye" class="emoji" data-type="emoji"><img src="https://cdn.jsdelivr.net/npm/emoji-datasource-apple/img/apple/64/1f61c.png" draggable="false" loading="lazy" align="absmiddle"></span></p><p>Here is a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/dannweeeee/jupiverse-kit">Github repository</a> that you can refer to in case you are stuck.</p><div class="relative header-and-anchor"><h2 id="h-conclusion">Conclusion</h2></div><p>Jupiter stands out as a game-changer in simplifying token swaps on the Solana blockchain. Its ability to abstract the complexities of liquidity aggregation and transaction serialization empowers developers to focus on creating seamless, user-friendly dApps. By providing robust APIs, streamlined integration, and support for advanced features like dynamic routing and referral fee mechanisms, Jupiter offers the tools necessary to elevate any Solana dApp to the next level without having to worry about the nitty-gritty of swaps.</p><p>As part of my passion for empowering builders, I’ve been working on a project aimed at accelerating development on Solana. Built on the foundation of Jupiter, this initiative aims to help builders bring their dApps to market faster while delivering exceptional user experiences.</p><p>Enter <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://jupiversekit.vercel.app/"><strong>Jupiverse Kit</strong></a>—an open-source ready-to-use React components library that consolidates all of Jupiter’s APIs, SDKs, and packages (such as @jup-ag/terminal, @jup-ag/api, @jup-ag/wallet-adapter, and @jup-ag/react-hooks) along with other Solana packages. This unified package simplifies integration and enables developers to hit the ground running with prebuilt components. </p><p><strong>P.S. Contributions are always welcome! This project is open to anyone in the Jupiverse who wants to collaborate and make an impact!</strong></p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a81343111923163f78c943a0ede28fca.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAARCAIAAAAzPjmrAAAACXBIWXMAABYlAAAWJQFJUiTwAAAE6UlEQVR4nI3UXWxTZRzH8bOxwSjd6TnPc85znvacnr6c05e167Z23dquHevaru3e3+he7KBjc9DhNhhjGyAwccBEYaKMAJMxdSJxQEiMIDHGG5ALiYmJkhgSExKNMfFKvfFuZoCIOpHv5XPzufjl+RMZy2USBCFKkui0IdlKQg1BZGRmZGauWpWxOitLkbWWVK5TgczsHEEQMMYEQWStWUdrjMhmwoV5UK3LViqApIl0pgYnXu9I7xXtHgUJWVHibBJBLUeTpIokc0mSVCqVKgpQgAMkhCyEPOIsasldZHaVM1jIyclZs3pNdnYWMjjrho/2XDiSfu+Uv6YHO43Jt8Ze/eaLxR+WLn+71D7+mmQPbpqa2bzwCgEggBBSFP1AokiSpCGCjJqlWIQRZ1Rr3TpHMGj3RRnMKxSKtQqFQrnO4qlPvbEwfvPM6NUrnsrnpGjB+M1zC7/98vnS0sc/LTUO7i/yt418cHn0swWCXg5QT0RDBABiaIbFCBmxtkhn8QR4vWNtjjKLIDIIIjs7y+Kp7zw+M3R9On1qzuwIONp8I5/Oz37/47Wfl2Zv/Vpa2+GLdPcvnOm/dP4hsNxjAEKOphALGAYjJKu1DkNxvLZ2e7owFraWByS3z+QJbZy4lDp7Zsvika4D04Jk96SjI5+8M3P3/pXvlg5e/NLqDVU0bnt+/njP/Lm/gEcGTUOWAxTLMMsAq0ecxEslZeH+Td5kky0SsYbitsqmxNjbG6YPt82MVadG1LK8frhl5Pr707fvzd3+vf/YBb2tNJoc3nh6suvE6RUAABCgIMOyNMdYS/O27R0b3j+1sTsdq4pWhcLxWKy5fdNLs5dm7ty4eO9OfdcOZOCr9nQNLc4fufHV8Q/vt+54WSu74r072958sXXixN+Ah3sAACEFGJahEchz25qSyZauvkhNQyQai9fW1jTWb0i9MHRsbnRxZuqjK1UN3ZxZiO/rTp+fPbB4a9/crUBLSiu741sHGyZ31gweWhkAFIAsZDBi1AwSBMSLSpJU5OQABhaUlwbCzc6mzvLtrRW9PVrZKbjl2Ojm3pMnd5291j95wbG+ymD1hXp7q3cPBJPj/wYeIQBCgABv1BnkfL3JodFKHBY4LCC1aMj3u+s2eJONntqERmcWfZbwUKrz4NSWw+92DB812F1mR6C8u61yYGtpXd/KAL08NaOicwvKgse+vpsaO1Sf6K9u3VoWaS/2NxV5G2wlsbyS2ODiVdHuEj2WYF9n88ierl3T4Y5tGoPZ4qoobasr60o5gon/BGgakKTS5vInBiZiiXSkqS+WHAi3p0OJvkBDyuGptRbHHIFm3mQ3+PJ8nS3R7oG67t3FoQaO11vdgYKaoKsuITuDTwFoilJpjRaXN2ovrrC7g3ZvyO6tsnkjNm/E7AzIhX6dXIJFg67YWlgd9Td2ltelTE4fh0W5qDQv7LOvr+ZNzqcBT/4+iqJUDw4WABDxosakF8wmjHnIsUK+Oa/c76ysdvrjgmThsNbgKJC8RUanH2ul/wHoFbZhEcacoFYLAsNyDMdqJIPebpcLPJLNxfE8hwXBbBJsFt6Uz2D+n6fiGRkAAYDM8plkGU6NsSBodJJaa2AQYlgOaTRYFDleD1lEKP8MYyyKWsRxj1+esVwyN5ckVRSlepAgG7RWE0XTpIpSKnP/AEWFREOolT/fAAAAAElFTkSuQmCC" nextheight="2072" nextwidth="3792" class="image-node embed"><figcaption htmlattributes="[object Object]" class=""><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://jupiversekit.vercel.app"><em><u>Jupiverse Kit</u></em></a></figcaption></figure><p>I hope this guide helps accelerate your development process and enables you to build more efficiently with Jupiter!</p><p>A huge shoutout to <strong>Soju</strong> for getting me involved with building with <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://jup.ag/">Jupiter</a> and inspiring me to share my experience through writing! The big question now is, ‘<em>Wen next article?</em>’ <span data-name="rofl" class="emoji" data-type="emoji"><img src="https://cdn.jsdelivr.net/npm/emoji-datasource-apple/img/apple/64/1f923.png" draggable="false" loading="lazy" align="absmiddle"></span></p>]]></content:encoded>
            <author>dannweeeee@newsletter.paragraph.com (based dann)</author>
            <category>solana</category>
            <category>jupiter</category>
            <category>swap</category>
            <category>typescript</category>
            <category>hooks</category>
            <category>zustand</category>
            <category>jupiverse-kit</category>
            <category>@jup-ag</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/a6501e69d962802adba1d6ca8c031357.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Programmatically Register Basenames for Your Users on Base]]></title>
            <link>https://blog.dannwee.xyz/programmatically-register-basenames-for-your-users-on-base</link>
            <guid>YykLR9bVBk7u2ikJfOsM</guid>
            <pubDate>Sun, 15 Dec 2024 16:00:00 GMT</pubDate>
            <description><![CDATA[What if navigating the onchain economy was as simple as remembering a username? Imagine a world where sending crypto, connecting with communities, and managing your digital assets are as intuitive as using social media. This isn't a distant dream - it's already taking shape, thanks to Basenames in the Base ecosystem. By turning complex wallet addresses into easy, human-readable names, Basenames are transforming how we interact with the onchain economy. They unlock a future where anyone, from ...]]></description>
            <content:encoded><![CDATA[<p><strong><em>What if navigating the onchain economy was as simple as remembering a username?</em></strong> </p><p>Imagine a world where sending crypto, connecting with communities, and managing your digital assets are as intuitive as using social media. This isn't a distant dream - it's already taking shape, thanks to <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.base.org/names"><strong>Basenames</strong></a> in the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.base.org/">Base</a> ecosystem.</p><p>By turning complex wallet addresses into easy, human-readable names, <strong><em>Basenames are transforming how we interact with the onchain economy</em></strong>. They unlock a future where anyone, from blockchain novices to seasoned pros, can engage seamlessly in a decentralized digital landscape. Whether you’re sending assets, collaborating on projects, or building your digital identity, Basenames are the gateway to a more intuitive, inclusive, and efficient blockchain experience.</p><p>In this Builder Guide, I’ll walk you through how to<strong> allow users of your dApp to programatically register for a Basename</strong>, along with the key considerations and best practices to keep in mind when building. Plus, I’ll also show you how to integrate <strong>CDP Paymaster</strong> to sponsor gas fees, making the user experience 1000x smoother. Whether you’re a seasoned developer or a newbie, this guide will equip you with the tools to build a more user-friendly onchain ecosystem.</p><div class="relative header-and-anchor"><h2 id="h-high-level-overview-of-basenames">High-Level Overview of Basenames</h2></div><p>Essentially, Basenames leverages the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ens.domains/">ENS (Ethereum Name Service)</a> and functions as human-readable subdomains (*.base.eth) where users can register and manage their onchain name.</p><p>The diagram below provides a high-level overview of how Basenames work, while the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base-org/basenames">open-source GitHub repository</a> delves into the detailed implementation, consisting of the contracts for resolving, managing, and tokenising subdomains as ERC721 tokens. These names are fully onchain, which means they are compatible across the Base network and other EVM-compatible blockchains, ensuring seamless cross-chain integration. With support for public resolvers, you as a developer can set various records (address, text, DNS) tied to your user's Basename, making it a versatile tool for dApps and decentralised identities.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e53a69b4b95d18827cd4a7b1fc3c5d52.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAOCAIAAADBvonlAAAACXBIWXMAAAsTAAALEwEAmpwYAAADQklEQVR4nJ2TUUhUWRjHv3PuOWfu9epN587MnbnKVDfHXhKGiUVhRpAUnyawYHwLNIMUKXbWmV2WYWvuoK7BhPuwD4JUsK2rtGVSBhLUUy89iPhURlEPvjUJpTNqTJ1l5kjoELT0fzoHzvf9vu/7fwfgu4QleqD+WI3rEADI1brr8A+aeUz1HEWYVD4dHBxcWVmZnZ1VFKUUiaWGhnrLsgzDwBgDgNPpDAQCwWDQ5/OpZVmW1dgY8HhNicgIIUmiEpF1w2w42OgxvAihfYCenp579+9ns1kBMAxjeXl5Y2NjZmZG1/Xq6upkMpnP53O5XDgcBoDOzs5cLlcobMXjPwIApVTUcckee/J0+dbdB4pSBQD7MIwx0zRdLhcA+Hy+16/fcM4XFhYopYQQ28585nxre6e9vR0AotHo+vr6Zj6fTCYFQOSyf//j0dMXt+49VlV1HwBjTAhxOByUUgDQdf369WuLi4sjIyOMMYTgRGfXb/ZYeuTKocNHACASibx//2Fn5+Pw8PAuoNzByV/u/PSQn7/xljlKk4CKQQkhhAghGGPGmKAihBhjbrf7S6WU0lOnTvf29rrdblEfQiWAdMCqaY7R+pavrUS5hKampps3/56fn49GowCQSPw8NTWVyWQMwwCAeDz+z8xs9urVlpaWbDY7OTnZ0dEBAIQQAT539sxf1/68MmY7HI5KD8TF7/f/mkrZmUwk0gYAExMTW9vb2zsfR0dHASAWi12+nB4YGIjFYsVikXNu2/Zek+fm5jjnudw70ZkkSd/YdEVRQqFQ6PjxQCAgihAx4XB4c3OzWCwmEom9gOnpac756uqqMFk4WmmAVJYI2L/OCGNJTEOW5ebm5mAwqOu62EBhj2mara2tXV1ddXV1GGPLskzTrMQghHBZ4kDKkiQJoV0eY0zXdcaYeFMxZMaYpmm1tbV7Ar+2SxWSqFzjsVT9YFVdPaXM6/V6PB7GGAD09fWlUql0Ot3WVrJNlksfGwBUVdU07dupdwGEVtX6FM1QNEPEY4xF70tLS58+c875+Pi4yAsAoVBobW2tUCh0d3f/X0bFGIUA4N/bt589f/7y5asLFy9+MdbpdPb39w8NDfn9fgD4D6wF30Z4fPxFAAAAAElFTkSuQmCC" nextheight="913" nextwidth="2021" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><div class="relative header-and-anchor"><h2 id="h-step-by-step-guide-implementing-basenames-in-your-dapp">Step-By-Step Guide: Implementing Basenames in your dApp</h2></div><p>For context, in this guide we will be on Base Sepolia network, using NextJS (Typescript) as the frontend framework, with Wagmi + Viem as the frontend hooks and low-level interface to interact with anything Ethereum.</p><p>Essentially, the main smart contract that you have to interact with to programatically register Basenames via your dApp is <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://sepolia.basescan.org/address/0xa0c70ec36c010b55e3c434d6c6ebeec50c705794#code">Registrar.sol</a> that contains the <code>register()</code> function.</p><p>However, it’s not as straightforward as simply passing a string into the register function to register the Basenames. There are numerous nuances to consider in preparing your arguments before properly passing into the function.</p><div class="relative header-and-anchor"><h3 id="h-prerequisites">Prerequisites</h3></div><ul><li><p><strong>Install the </strong><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.npmjs.com/package/@coinbase/coinbase-sdk"><strong>CDP SDK</strong></a><strong>.</strong></p></li><li><p><strong>Install </strong><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://onchainkit.xyz/installation/nextjs"><strong>Onchainkit</strong></a><strong>.</strong></p></li><li><p><strong>Have an 'ETH funded' wallet (minimum of 0.005 ETH) to get started.</strong><br><em>Reason: The </em><code>_validatePayment()</code> function in the <code>RegistrarController.sol</code> has a require statement that requires the <code>msg.value</code> to be more than the <code>price</code> of the Basename.</p><pre data-type="codeBlock" text="    /// @notice Internal helper for validating ETH payments
    ///
    /// @dev Emits `ETHPaymentProcessed` after validating the payment.
    ///
    /// @param price The expected value.
    function _validatePayment(uint256 price) internal {
        if (msg.value < price) {
            revert InsufficientValue();
        }
        emit ETHPaymentProcessed(msg.sender, price);
    }"><code>    <span class="hljs-comment">/// @notice Internal helper for validating ETH payments</span>
    <span class="hljs-comment">///</span>
    <span class="hljs-comment">/// @dev Emits `ETHPaymentProcessed` after validating the payment.</span>
    <span class="hljs-comment">///</span>
    <span class="hljs-comment">/// @param price The expected value.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_validatePayment</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> price</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> </span>{
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span> <span class="hljs-operator">&lt;</span> price) {
            <span class="hljs-keyword">revert</span> InsufficientValue();
        }
        <span class="hljs-keyword">emit</span> ETHPaymentProcessed(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, price);
    }</code></pre></li></ul><div class="relative header-and-anchor"><h3 id="h-create-basenamets-and-import-relevant-modules">Create <code>basename.ts</code> and Import Relevant Modules </h3></div><p>Create a <code>basename.ts</code> in your <code>lib</code> folder and import the following modules</p><pre data-type="codeBlock" text="import {
  encodeFunctionData,
  namehash,
  Address,
  createPublicClient,
  http,
} from &quot;viem&quot;;
import { baseSepolia, base } from &quot;viem/chains&quot;;
import { Basename } from &quot;@coinbase/onchainkit/identity&quot;;"><code><span class="hljs-keyword">import</span> {
  encodeFunctionData,
  namehash,
  <span class="hljs-title class_">Address</span>,
  createPublicClient,
  http,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"viem"</span>;
<span class="hljs-keyword">import</span> { baseSepolia, base } <span class="hljs-keyword">from</span> <span class="hljs-string">"viem/chains"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title class_">Basename</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">"@coinbase/onchainkit/identity"</span>;</code></pre><div class="relative header-and-anchor"><h3 id="h-define-contract-abis-and-contract-addresses">Define Contract ABIs and Contract Addresses</h3></div><p>In your <code>basename.ts</code> file, include the following code, which specifies the contract addresses for <code>RegistrarController.sol</code> and <code>L2Resolver.sol</code>, along with the ABIs for each of these contracts.</p><pre data-type="codeBlock" text="// Chain ID
export const BASE_SEPOLIA_CHAIN_ID = 84532

// Contract Addresses
export const BASE_SEPOLIA_REGISTRAR_CONTROLLER_ADDRESS =
	&quot;0x49ae3cc2e3aa768b1e5654f5d3c6002144a59581&quot;;
export const BASE_SEPOLIA_L2_RESOLVER_ADDRESS =
	&quot;0x6533C94869D28fAA8dF77cc63f9e2b2D6Cf77eBA&quot;;

// ABIs
export const BASE_SEPOLIA_L2_RESOLVER_ABI = [
  {
    inputs: [
      { internalType: &quot;bytes32&quot;, name: &quot;node&quot;, type: &quot;bytes32&quot; },
      { internalType: &quot;uint256&quot;, name: &quot;coinType&quot;, type: &quot;uint256&quot; },
      { internalType: &quot;bytes&quot;, name: &quot;a&quot;, type: &quot;bytes&quot; },
    ],
    name: &quot;setAddr&quot;,
    outputs: [],
    stateMutability: &quot;nonpayable&quot;,
    type: &quot;function&quot;,
  },
  {
    inputs: [
      { internalType: &quot;bytes32&quot;, name: &quot;node&quot;, type: &quot;bytes32&quot; },
      { internalType: &quot;string&quot;, name: &quot;newName&quot;, type: &quot;string&quot; },
    ],
    name: &quot;setName&quot;,
    outputs: [],
    stateMutability: &quot;nonpayable&quot;,
    type: &quot;function&quot;,
  },
];

export const BASE_SEPOLIA_REGISTRAR_CONTROLLER_ABI = [
   {
    inputs: [
      {
        components: [
          { internalType: &quot;string&quot;, name: &quot;name&quot;, type: &quot;string&quot; },
          { internalType: &quot;address&quot;, name: &quot;owner&quot;, type: &quot;address&quot; },
          { internalType: &quot;uint256&quot;, name: &quot;duration&quot;, type: &quot;uint256&quot; },
          { internalType: &quot;address&quot;, name: &quot;resolver&quot;, type: &quot;address&quot; },
          { internalType: &quot;bytes[]&quot;, name: &quot;data&quot;, type: &quot;bytes[]&quot; },
          { internalType: &quot;bool&quot;, name: &quot;reverseRecord&quot;, type: &quot;bool&quot; },
        ],
        internalType: &quot;struct RegistrarController.RegisterRequest&quot;,
        name: &quot;request&quot;,
        type: &quot;tuple&quot;,
      },
    ],
    name: &quot;register&quot;,
    outputs: [],
    stateMutability: &quot;payable&quot;,
    type: &quot;function&quot;,
  },
  {
    inputs: [
      { internalType: &quot;string&quot;, name: &quot;name&quot;, type: &quot;string&quot; },
      { internalType: &quot;uint256&quot;, name: &quot;duration&quot;, type: &quot;uint256&quot; },
    ],
    name: &quot;registerPrice&quot;,
    outputs: [{ internalType: &quot;uint256&quot;, name: &quot;&quot;, type: &quot;uint256&quot; }],
    stateMutability: &quot;view&quot;,
    type: &quot;function&quot;,
  },
];"><code><span class="hljs-comment">// Chain ID</span>
export <span class="hljs-keyword">const</span> BASE_SEPOLIA_CHAIN_ID = <span class="hljs-number">84532</span>

<span class="hljs-comment">// Contract Addresses</span>
export <span class="hljs-keyword">const</span> BASE_SEPOLIA_REGISTRAR_CONTROLLER_ADDRESS =
	<span class="hljs-string">"0x49ae3cc2e3aa768b1e5654f5d3c6002144a59581"</span>;
export <span class="hljs-keyword">const</span> BASE_SEPOLIA_L2_RESOLVER_ADDRESS =
	<span class="hljs-string">"0x6533C94869D28fAA8dF77cc63f9e2b2D6Cf77eBA"</span>;

<span class="hljs-comment">// ABIs</span>
export <span class="hljs-keyword">const</span> BASE_SEPOLIA_L2_RESOLVER_ABI = [
  {
    inputs: [
      { internalType: <span class="hljs-string">"bytes32"</span>, name: <span class="hljs-string">"node"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"bytes32"</span> },
      { internalType: <span class="hljs-string">"uint256"</span>, name: <span class="hljs-string">"coinType"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"uint256"</span> },
      { internalType: <span class="hljs-string">"bytes"</span>, name: <span class="hljs-string">"a"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"bytes"</span> },
    ],
    name: <span class="hljs-string">"setAddr"</span>,
    outputs: [],
    stateMutability: <span class="hljs-string">"nonpayable"</span>,
    <span class="hljs-keyword">type</span>: <span class="hljs-string">"function"</span>,
  },
  {
    inputs: [
      { internalType: <span class="hljs-string">"bytes32"</span>, name: <span class="hljs-string">"node"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"bytes32"</span> },
      { internalType: <span class="hljs-string">"string"</span>, name: <span class="hljs-string">"newName"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"string"</span> },
    ],
    name: <span class="hljs-string">"setName"</span>,
    outputs: [],
    stateMutability: <span class="hljs-string">"nonpayable"</span>,
    <span class="hljs-keyword">type</span>: <span class="hljs-string">"function"</span>,
  },
];

export <span class="hljs-keyword">const</span> BASE_SEPOLIA_REGISTRAR_CONTROLLER_ABI = [
   {
    inputs: [
      {
        components: [
          { internalType: <span class="hljs-string">"string"</span>, name: <span class="hljs-string">"name"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"string"</span> },
          { internalType: <span class="hljs-string">"address"</span>, name: <span class="hljs-string">"owner"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"address"</span> },
          { internalType: <span class="hljs-string">"uint256"</span>, name: <span class="hljs-string">"duration"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"uint256"</span> },
          { internalType: <span class="hljs-string">"address"</span>, name: <span class="hljs-string">"resolver"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"address"</span> },
          { internalType: <span class="hljs-string">"bytes[]"</span>, name: <span class="hljs-string">"data"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"bytes[]"</span> },
          { internalType: <span class="hljs-string">"bool"</span>, name: <span class="hljs-string">"reverseRecord"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"bool"</span> },
        ],
        internalType: <span class="hljs-string">"struct RegistrarController.RegisterRequest"</span>,
        name: <span class="hljs-string">"request"</span>,
        <span class="hljs-keyword">type</span>: <span class="hljs-string">"tuple"</span>,
      },
    ],
    name: <span class="hljs-string">"register"</span>,
    outputs: [],
    stateMutability: <span class="hljs-string">"payable"</span>,
    <span class="hljs-keyword">type</span>: <span class="hljs-string">"function"</span>,
  },
  {
    inputs: [
      { internalType: <span class="hljs-string">"string"</span>, name: <span class="hljs-string">"name"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"string"</span> },
      { internalType: <span class="hljs-string">"uint256"</span>, name: <span class="hljs-string">"duration"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"uint256"</span> },
    ],
    name: <span class="hljs-string">"registerPrice"</span>,
    outputs: [{ internalType: <span class="hljs-string">"uint256"</span>, name: <span class="hljs-string">""</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"uint256"</span> }],
    stateMutability: <span class="hljs-string">"view"</span>,
    <span class="hljs-keyword">type</span>: <span class="hljs-string">"function"</span>,
  },
];</code></pre><div class="relative header-and-anchor"><h3 id="h-function-to-create-arguments-for-register-method">Function to Create Arguments for <code>register()</code> method</h3></div><p>Before implementing the function to generate arguments for the <code>register()</code> method, we first need to define a constant <code>USERNAME_DOMAINS</code> to record the domain name associated with the network. For Base Sepolia, this will be <code>basetest.eth</code>, and for Base, it will be <code>base.eth</code>.</p><p>Next, we will create a helper function, <code>formatBaseEthDomain()</code>, to properly format the Base domain.</p><p>Finally, we'll construct the arguments for the <code>register()</code> method in <code>RegistrarController.sol</code>. These arguments will leverage the <code>setName()</code> and <code>setAddr()</code> methods from <code>L2Resolver.sol</code>.</p><pre data-type="codeBlock" text="// username domains
export const USERNAME_DOMAINS: Record<number, string> = {
  [baseSepolia.id]: &quot;basetest.eth&quot;,
  [base.id]: &quot;base.eth&quot;,
};

// format base eth domain name
export const formatBaseEthDomain = (
  name: string,
  chainId: number
): Basename => {
  return `${name}.${
    USERNAME_DOMAINS[chainId] ?? &quot;.base.eth&quot;
  }`.toLocaleLowerCase() as Basename;
};

// Create register contract method arguments
export function createRegisterContractMethodArgs(
  baseName: string,
  addressId: Address
): RegistrationArgs {
  const addressData = encodeFunctionData({
    abi: L2ResolverAbi,
    functionName: &quot;setAddr&quot;,
    args: [namehash(formatBaseEthDomain(baseName, baseSepolia.id)), addressId],
  });

  const nameData = encodeFunctionData({
    abi: L2ResolverAbi,
    functionName: &quot;setName&quot;,
    args: [
      namehash(formatBaseEthDomain(baseName, baseSepolia.id)),
      formatBaseEthDomain(baseName, baseSepolia.id),
    ],
  });

  const registerArgs: RegistrationArgs = {
    name: baseName,
    owner: addressId,
    duration: BigInt(31557600),
    resolver: BASE_SEPOLIA_L2_RESOLVER_ADDRESS,
    data: [addressData, nameData],
    reverseRecord: true,
  };

  console.log(`Register contract method arguments constructed: `, registerArgs);

  return registerArgs;
}"><code><span class="hljs-comment">// username domains</span>
export const USERNAME_DOMAINS: Record<span class="hljs-operator">&lt;</span>number, <span class="hljs-keyword">string</span><span class="hljs-operator">&gt;</span> <span class="hljs-operator">=</span> {
  [baseSepolia.id]: <span class="hljs-string">"basetest.eth"</span>,
  [base.id]: <span class="hljs-string">"base.eth"</span>,
};

<span class="hljs-comment">// format base eth domain name</span>
export const formatBaseEthDomain <span class="hljs-operator">=</span> (
  name: <span class="hljs-keyword">string</span>,
  chainId: number
): Basename <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  <span class="hljs-keyword">return</span> `${name}.${
    USERNAME_DOMAINS[chainId] ?? <span class="hljs-string">".base.eth"</span>
  }`.toLocaleLowerCase() <span class="hljs-keyword">as</span> Basename;
};

<span class="hljs-comment">// Create register contract method arguments</span>
export <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createRegisterContractMethodArgs</span>(<span class="hljs-params">
  baseName: <span class="hljs-keyword">string</span>,
  addressId: Address
</span>): <span class="hljs-title">RegistrationArgs</span> </span>{
  const addressData <span class="hljs-operator">=</span> encodeFunctionData({
    <span class="hljs-built_in">abi</span>: L2ResolverAbi,
    functionName: <span class="hljs-string">"setAddr"</span>,
    args: [namehash(formatBaseEthDomain(baseName, baseSepolia.id)), addressId],
  });

  const nameData <span class="hljs-operator">=</span> encodeFunctionData({
    <span class="hljs-built_in">abi</span>: L2ResolverAbi,
    functionName: <span class="hljs-string">"setName"</span>,
    args: [
      namehash(formatBaseEthDomain(baseName, baseSepolia.id)),
      formatBaseEthDomain(baseName, baseSepolia.id),
    ],
  });

  const registerArgs: RegistrationArgs <span class="hljs-operator">=</span> {
    name: baseName,
    owner: addressId,
    duration: BigInt(<span class="hljs-number">31557600</span>),
    resolver: BASE_SEPOLIA_L2_RESOLVER_ADDRESS,
    data: [addressData, nameData],
    reverseRecord: <span class="hljs-literal">true</span>,
  };

  console.log(`Register <span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">method</span> <span class="hljs-title">arguments</span> <span class="hljs-title">constructed</span>: `, <span class="hljs-title">registerArgs</span>);

  <span class="hljs-title"><span class="hljs-keyword">return</span></span> <span class="hljs-title">registerArgs</span>;
}</span></code></pre><div class="relative header-and-anchor"><h3 id="h-function-to-estimate-basename-registration-price">Function to Estimate Basename Registration Price</h3></div><p>As noted earlier, the <code>_validatePayment()</code> function in <code>RegistrarController.sol</code> includes a require statement ensuring that <code>msg.value</code> is greater than the price of the basename. To ensure the user sends enough ETH when calling the register() function, we will create a final helper function in <code>basename.ts</code> called <code>estimateMintValue()</code>. This function will be used to estimate the amount of ETH required to pay when minting a basename.</p><pre data-type="codeBlock" text="// Estimate registration price
export async function estimateMintValue(
  baseName: string,
  duration: bigint
): Promise<bigint> {
  const publicClient = createPublicClient({
    chain: baseSepolia,
    transport: http(),
  });

  const price = await publicClient.readContract({
    address: BASE_SEPOLIA_REGISTRAR_CONTROLLER_ADDRESS,
    abi: RegistrarControllerAbi,
    functionName: &quot;registerPrice&quot;,
    args: [baseName, duration],
  });

  console.log(`Estimated mint value for ${baseName}: `, price);

  return price;
}"><code><span class="hljs-comment">// Estimate registration price</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">estimateMintValue</span>(<span class="hljs-params">
  baseName: <span class="hljs-built_in">string</span>,
  duration: <span class="hljs-built_in">bigint</span>
</span>): <span class="hljs-title class_">Promise</span>&lt;<span class="hljs-built_in">bigint</span>&gt; {
  <span class="hljs-keyword">const</span> publicClient = <span class="hljs-title function_">createPublicClient</span>({
    <span class="hljs-attr">chain</span>: baseSepolia,
    <span class="hljs-attr">transport</span>: <span class="hljs-title function_">http</span>(),
  });

  <span class="hljs-keyword">const</span> price = <span class="hljs-keyword">await</span> publicClient.<span class="hljs-title function_">readContract</span>({
    <span class="hljs-attr">address</span>: <span class="hljs-variable constant_">BASE_SEPOLIA_REGISTRAR_CONTROLLER_ADDRESS</span>,
    <span class="hljs-attr">abi</span>: <span class="hljs-title class_">RegistrarControllerAbi</span>,
    <span class="hljs-attr">functionName</span>: <span class="hljs-string">"registerPrice"</span>,
    <span class="hljs-attr">args</span>: [baseName, duration],
  });

  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`Estimated mint value for <span class="hljs-subst">${baseName}</span>: `</span>, price);

  <span class="hljs-keyword">return</span> price;
}</code></pre><div class="relative header-and-anchor"><h3 id="h-onchainkit-lesstransaction-greater-component-w-paymaster">Onchainkit <code>&lt;Transaction /&gt;</code> Component w/ Paymaster</h3></div><p>Next, in <code>register-basename.tsx</code> component, we will utilize the <strong>Onchainkit Transaction Component</strong> to simplify the integration of basename registration on the frontend. You will need your <strong>Paymaster Endpoint</strong> that you can get from <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://portal.cdp.coinbase.com/">Coinbase Developer Platform</a>. </p><p>Here's how you can set up the <code>register-basename.tsx</code> component:</p><p><em>To further customize the </em><code>&lt;Transaction /&gt;</code><em> component to suit your preferences, you may refer to the </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://onchainkit.xyz/transaction/transaction"><em>Onchainkit documentation</em></a><em>. It provides guidance on tailoring the component to meet your specific requirements.</em></p><pre data-type="codeBlock" text="&quot;use client&quot;;

import React, { useMemo, useEffect, useState } from &quot;react&quot;;
import { encodeFunctionData } from &quot;viem&quot;;
import { useAccount } from &quot;wagmi&quot;;
import { Input } from &quot;@/components/ui/input&quot;;
import {
  Transaction,
  TransactionButton,
  TransactionError,
  TransactionResponse,
  TransactionStatus,
  TransactionStatusAction,
  TransactionStatusLabel,
} from &quot;@coinbase/onchainkit/transaction&quot;;
import {
  createRegisterContractMethodArgs,
  estimateMintValue,
  BASE_SEPOLIA_CHAIN_ID,
  BASE_SEPOLIA_REGISTRAR_CONTROLLER_ADDRESS,
  RegistrarControllerAbi,
} from &quot;../basename&quot;;

interface RegistrationArgs {
  name: string;
  owner: `0x${string}`;
  duration: bigint;
  resolver: `0x${string}`;
  data: readonly `0x${string}`[];
  reverseRecord: boolean;
}

const RegistrationForm = () => {
  const [baseName, setBaseName] = useState(&quot;&quot;);
  const [registrationArgs, setRegistrationArgs] =
    useState<RegistrationArgs | null>(null);
  const [estimatedValue, setEstimatedValue] = useState<number | null>(null);
  const account = useAccount();

  // Handle input change
  const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setBaseName(event.target.value);
  };

  // Fetch registration data whenever baseName or account address changes
  useEffect(() => {
    const fetchRegistrationData = async () => {
      if (baseName &amp;&amp; account.address) {
        const args = createRegisterContractMethodArgs(
          baseName,
          account.address
        );
        const value = await estimateMintValue(baseName, args.duration);
        setRegistrationArgs(args);
        setEstimatedValue(Number(value));
      }
    };

    fetchRegistrationData();
  }, [baseName, account.address]);

  // Prepare the transaction call
  const registerBasenameCall = useMemo(() => {
    if (
      !BASE_SEPOLIA_REGISTRAR_CONTROLLER_ADDRESS ||
      !account.address ||
      !registrationArgs ||
      estimatedValue === null
    ) {
      return [];
    }
    return [
      {
        to: BASE_SEPOLIA_REGISTRAR_CONTROLLER_ADDRESS as `0x${string}`,
        data: encodeFunctionData({
          abi: RegistrarControllerAbi,
          functionName: &quot;register&quot;,
          args: [
            {
              name: registrationArgs.name,
              owner: registrationArgs.owner,
              duration: registrationArgs.duration,
              resolver: registrationArgs.resolver,
              data: registrationArgs.data,
              reverseRecord: registrationArgs.reverseRecord,
            },
          ],
        }),
        value: BigInt(estimatedValue),
      },
    ];
  }, [registrationArgs, account.address, estimatedValue]);

  // Handle transaction success
  const handleSuccess = async (transactionResponse: TransactionResponse) => {
    console.log(&quot;Transaction Response:&quot;, transactionResponse);
  };

  // Handle transaction error
  const handleError = (error: TransactionError) => {
    console.error(&quot;Transaction Error:&quot;, error);
  };

  return (
    <div>
      <Input
        placeholder=&quot;Enter base name&quot;
        value={baseName}
        onChange={handleInputChange}
      />
      <Transaction
        chainId={BASE_SEPOLIA_CHAIN_ID}
        calls={registerBasenameCall}
        onError={handleError}
        onSuccess={handleSuccess}
        capabilities={{
          paymasterService: {
            url: process.env
              .NEXT_PUBLIC_CDP_PAYMASTER_AND_BUNDLER_ENDPOINT as string,
          },
        }}
      >
        <TransactionButton text=&quot;Register&quot; />
        <TransactionStatus>
          <TransactionStatusLabel />
          <TransactionStatusAction />
        </TransactionStatus>
      </Transaction>
    </div>
  );
};

export default RegistrationForm;"><code><span class="hljs-string">"use client"</span>;

<span class="hljs-keyword">import</span> <span class="hljs-title">React</span>, { <span class="hljs-title">useMemo</span>, <span class="hljs-title">useEffect</span>, <span class="hljs-title">useState</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">encodeFunctionData</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"viem"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">useAccount</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"wagmi"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">Input</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@/components/ui/input"</span>;
<span class="hljs-keyword">import</span> {
  <span class="hljs-title">Transaction</span>,
  <span class="hljs-title">TransactionButton</span>,
  <span class="hljs-title">TransactionError</span>,
  <span class="hljs-title">TransactionResponse</span>,
  <span class="hljs-title">TransactionStatus</span>,
  <span class="hljs-title">TransactionStatusAction</span>,
  <span class="hljs-title">TransactionStatusLabel</span>,
} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@coinbase/onchainkit/transaction"</span>;
<span class="hljs-keyword">import</span> {
  <span class="hljs-title">createRegisterContractMethodArgs</span>,
  <span class="hljs-title">estimateMintValue</span>,
  <span class="hljs-title">BASE_SEPOLIA_CHAIN_ID</span>,
  <span class="hljs-title">BASE_SEPOLIA_REGISTRAR_CONTROLLER_ADDRESS</span>,
  <span class="hljs-title">RegistrarControllerAbi</span>,
} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../basename"</span>;

<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">RegistrationArgs</span> </span>{
  name: <span class="hljs-keyword">string</span>;
  owner: `0x${<span class="hljs-keyword">string</span>}`;
  duration: bigint;
  resolver: `0x${<span class="hljs-keyword">string</span>}`;
  data: readonly `0x${<span class="hljs-keyword">string</span>}`[];
  reverseRecord: boolean;
}

const RegistrationForm <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  const [baseName, setBaseName] <span class="hljs-operator">=</span> useState(<span class="hljs-string">""</span>);
  const [registrationArgs, setRegistrationArgs] <span class="hljs-operator">=</span>
    useState<span class="hljs-operator">&lt;</span>RegistrationArgs <span class="hljs-operator">|</span> null<span class="hljs-operator">&gt;</span>(null);
  const [estimatedValue, setEstimatedValue] <span class="hljs-operator">=</span> useState<span class="hljs-operator">&lt;</span>number <span class="hljs-operator">|</span> null<span class="hljs-operator">&gt;</span>(null);
  const account <span class="hljs-operator">=</span> useAccount();

  <span class="hljs-comment">// Handle input change</span>
  const handleInputChange <span class="hljs-operator">=</span> (<span class="hljs-function"><span class="hljs-keyword">event</span>: <span class="hljs-title">React</span>.<span class="hljs-title">ChangeEvent</span>&lt;<span class="hljs-title">HTMLInputElement</span>&gt;) =&gt; </span>{
    setBaseName(<span class="hljs-keyword">event</span>.target.<span class="hljs-built_in">value</span>);
  };

  <span class="hljs-comment">// Fetch registration data whenever baseName or account address changes</span>
  useEffect(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    const fetchRegistrationData <span class="hljs-operator">=</span> async () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
      <span class="hljs-keyword">if</span> (baseName <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> account.<span class="hljs-built_in">address</span>) {
        const args <span class="hljs-operator">=</span> createRegisterContractMethodArgs(
          baseName,
          account.<span class="hljs-built_in">address</span>
        );
        const value <span class="hljs-operator">=</span> await estimateMintValue(baseName, args.duration);
        setRegistrationArgs(args);
        setEstimatedValue(Number(value));
      }
    };

    fetchRegistrationData();
  }, [baseName, account.<span class="hljs-built_in">address</span>]);

  <span class="hljs-comment">// Prepare the transaction call</span>
  const registerBasenameCall <span class="hljs-operator">=</span> useMemo(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    <span class="hljs-keyword">if</span> (
      <span class="hljs-operator">!</span>BASE_SEPOLIA_REGISTRAR_CONTROLLER_ADDRESS <span class="hljs-operator">|</span><span class="hljs-operator">|</span>
      <span class="hljs-operator">!</span>account.<span class="hljs-built_in">address</span> <span class="hljs-operator">|</span><span class="hljs-operator">|</span>
      <span class="hljs-operator">!</span>registrationArgs <span class="hljs-operator">|</span><span class="hljs-operator">|</span>
      estimatedValue <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> null
    ) {
      <span class="hljs-keyword">return</span> [];
    }
    <span class="hljs-keyword">return</span> [
      {
        to: BASE_SEPOLIA_REGISTRAR_CONTROLLER_ADDRESS <span class="hljs-keyword">as</span> `0x${<span class="hljs-keyword">string</span>}`,
        data: encodeFunctionData({
          <span class="hljs-built_in">abi</span>: RegistrarControllerAbi,
          functionName: <span class="hljs-string">"register"</span>,
          args: [
            {
              name: registrationArgs.<span class="hljs-built_in">name</span>,
              owner: registrationArgs.owner,
              duration: registrationArgs.duration,
              resolver: registrationArgs.resolver,
              data: registrationArgs.data,
              reverseRecord: registrationArgs.reverseRecord,
            },
          ],
        }),
        <span class="hljs-built_in">value</span>: BigInt(estimatedValue),
      },
    ];
  }, [registrationArgs, account.<span class="hljs-built_in">address</span>, estimatedValue]);

  <span class="hljs-comment">// Handle transaction success</span>
  const handleSuccess <span class="hljs-operator">=</span> async (transactionResponse: TransactionResponse) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    console.log(<span class="hljs-string">"Transaction Response:"</span>, transactionResponse);
  };

  <span class="hljs-comment">// Handle transaction error</span>
  const handleError <span class="hljs-operator">=</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>: <span class="hljs-title">TransactionError</span>) =&gt; </span>{
    console.error(<span class="hljs-string">"Transaction Error:"</span>, <span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
  };

  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&lt;</span>div<span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span>Input
        placeholder<span class="hljs-operator">=</span><span class="hljs-string">"Enter base name"</span>
        value<span class="hljs-operator">=</span>{baseName}
        onChange<span class="hljs-operator">=</span>{handleInputChange}
      <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span>Transaction
        chainId<span class="hljs-operator">=</span>{BASE_SEPOLIA_CHAIN_ID}
        calls<span class="hljs-operator">=</span>{registerBasenameCall}
        onError<span class="hljs-operator">=</span>{handleError}
        onSuccess<span class="hljs-operator">=</span>{handleSuccess}
        capabilities<span class="hljs-operator">=</span>{{
          paymasterService: {
            url: process.env
              .NEXT_PUBLIC_CDP_PAYMASTER_AND_BUNDLER_ENDPOINT <span class="hljs-keyword">as</span> <span class="hljs-keyword">string</span>,
          },
        }}
      <span class="hljs-operator">&gt;</span>
        <span class="hljs-operator">&lt;</span>TransactionButton text<span class="hljs-operator">=</span><span class="hljs-string">"Register"</span> <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
        <span class="hljs-operator">&lt;</span>TransactionStatus<span class="hljs-operator">&gt;</span>
          <span class="hljs-operator">&lt;</span>TransactionStatusLabel <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
          <span class="hljs-operator">&lt;</span>TransactionStatusAction <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
        <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>TransactionStatus<span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>Transaction<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
  );
};

export default RegistrationForm;</code></pre><p>This <code>&lt;RegistrationForm /&gt;</code> component will allow users to register a basename programatically. The form utilizes the Coinbase Transaction component to handle the blockchain interaction, including transaction status and error handling.</p><p><strong>Key Functions of </strong><code>&lt;RegistrationForm /&gt;</code> </p><ul><li><p><code>fetchRegistrationData()</code> - responsible for preparing the contract call arguments and estimating the gas fee required for the transaction. It is triggered whenever baseName or account.address changes. It is put in a <code>useEffect</code> hook and contains the helper functions <code>estimateMintValue()</code> and <code>createRegisterContractMethodArgs()</code>.</p></li><li><p><code>registerBasenameCall()</code> - uses <code>useMemo</code> to create the transaction data only when all required information is available.</p></li><li><p><code>handleSuccess()</code> - triggered when the transaction is successfully completed. It logs the transaction response to the console. <strong><em>(You can add additional logic in this function)</em></strong></p></li><li><p><code>handleError()</code> - triggered if the transaction fails. It logs the error details. <strong><em>(You can add additional logic in this function)</em></strong></p></li></ul><p><strong>Putting it all together in the </strong><code>&lt;Transaction /&gt;</code> <strong>component, </strong>this component from Onchainkit handles the process of sending the transaction to the blockchain. It uses the callback functions defined earlier to manage the success and error states.</p><div class="relative header-and-anchor"><h2 id="h-conclusion">Conclusion</h2></div><p>Building the onchain economy is not just an aspiration; it’s happening now. By simplifying the process of obtaining a decentralized identity via your dApp, Basenames can unlock new opportunities for developers and users alike. </p><p>I hope this guide is able to make your developer experience on Base 1000x smoother! <span data-name="halo" class="emoji" data-type="emoji"><img src="https://cdn.jsdelivr.net/npm/emoji-datasource-apple/img/apple/64/1f607.png" draggable="false" loading="lazy" align="absmiddle"></span></p><p>Special thanks to <strong>Jesse Pollak</strong>, <strong>Will Binns</strong>, <strong>Wilson Cusack</strong>, <strong>Tina He</strong>, and the entire <strong>Onchainkit and Coinbase Developer Platform team</strong> for their invaluable guidance throughout this project. </p><p>There’s never been a better time to start building on Base <span data-name="laughing" class="emoji" data-type="emoji">😆</span></p>]]></content:encoded>
            <author>dannweeeee@newsletter.paragraph.com (based dann)</author>
            <category>basenames</category>
            <category>onchainkit</category>
            <category>coinbase</category>
            <category>cdp</category>
            <category>wagmi</category>
            <category>viem</category>
            <category>nextjs</category>
            <category>typescript</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/0a732725ea667dc4c9958292ee1bdee3.jpg" length="0" type="image/jpg"/>
        </item>
    </channel>
</rss>