<?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>Base Engineering Blog</title>
        <link>https://blog.base.dev</link>
        <description>Articles from the Base Engineering team about our work to build a global onchain economy that increases innovation, creativity, and freedom</description>
        <lastBuildDate>Wed, 27 May 2026 04:03:53 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>Base Engineering Blog</title>
            <url>https://storage.googleapis.com/papyrus_images/cb8a674c5b046323a9b6cffe12800ef73021a0d03f5983b648275e8236fb8363.jpg</url>
            <link>https://blog.base.dev</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[Multiproofs on Base]]></title>
            <link>https://blog.base.dev/multiproofs-on-base</link>
            <guid>AKWz1qqJ9CRS2MrO4zeI</guid>
            <pubDate>Thu, 21 May 2026 20:49:36 GMT</pubDate>
            <description><![CDATA[We have launched multiproofs on mainnet, which enables fast finality for withdrawals, brings us closer to Stage 2 decentralization, and increases our security posture.]]></description>
            <content:encoded><![CDATA[<h2 id="h-the-path-towards-decentralization" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Path Towards Decentralization</h2><p>Decentralization is one of the most important goals and principals at Base, as we strongly believe that this is what a global, free, onchain economy requires. We took the initial step towards this goal with:</p><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.base.org/fault-proofs-are-now-live-on-base-mainnet">Fault Proofs</a>: In October 2024, a decentralized proof system was set up allowing anyone to move funds between Ethereum and Base without relying on trusted authorities. Any user can permissionlessly make a claim as to the state of Base and any user can challenge a claim.</p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.base.org/base-has-reached-stage-1-decentralization">Security Council</a>: In April 2025, a decentralized committee reflecting Base’s values was set up to approve Base upgrades.</p></li></ul><p>With the above, we achieved Stage 1 decentralization, the first milestone in Vitalik Buterin’s <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ethereum-magicians.org/t/proposed-milestones-for-rollups-taking-off-training-wheels/11571">decentralization framework</a>.</p><p>We have now set our sights on the next and final decentralization milestone: Stage 2. A technical requirement of stage 2 decentralization is the ability to detect and handle bugs onchain. Our new multiproof system consisting of TEE and ZK provers not only accomplishes this, but also:</p><ul><li><p>Improves user experience and liquidity efficiency by enabling faster withdrawals.</p></li><li><p>Improves security as less financial resources are now required to defend against attacks.</p></li></ul><p>As the main component for our journey towards Stage 2, this new system expresses Base’s commitment to decentralization as well as our security-first and user-first mindset. Below, we explore the specifics of various proof systems to better understand the technological landscape that informed our multiproof solution.</p><h2 id="h-proof-systems" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Proof Systems</h2><h3 id="h-optimistic-proof-systems" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Optimistic Proof Systems</h3><p>Along with several other L2s, we started our path towards decentralization with an optimistic proof system. In this design, participants make a proposal as to what the state of Base is and this claim is optimistically accepted as the truth. If it is an invalid proposal, someone must actively challenge it, in which a dispute protocol would take place to determine who is correct.</p><p>While this system was an excellent initial step towards moving trust from central authorities to code, it faced certain limitations:</p><ul><li><p><strong>Heavily Involved Process:</strong> The dispute protocol is an interactive process where it may take dozens of transactions for a dispute to resolve. Due to this and the possibility of delay attacks, enough time had to be given to allow honest defenders to respond.</p></li><li><p><strong>Onchain Complexity:</strong> The dispute protocol for resolving challenges is very complex. In the case of our previous fault dispute system, it needed a smart contract that mimics how a virtual machine would operate. This was a very difficult problem to solve and became hard to maintain.</p></li></ul><p><strong>Bond Requirements:</strong> To deter incorrect actions, participants needed to put up bonds, where one would lose their bond if found to be dishonest. The previous fault dispute system required hundreds of ETH in bonds if fully played out, straining the resources of honest defenders in the worst case.</p><h3 id="h-validity-proof-systems" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Validity Proof Systems</h3><p>Validity systems are where proving the state of a chain is required, as opposed to an optimistic system where verification generally never occurs and is only needed if there is disagreement among participants. In particular, every block in a proposal is proved to be correct.</p><p>These systems solve some of the issues optimistic systems face, such as:</p><ul><li><p><strong>Non-Interactive Process</strong>: Proofs are generated offchain and verified onchain in a single transaction, no longer needing to provide time for participants to perform several steps. As a result, the complexity of these systems is mainly placed in offchain components.</p></li><li><p><strong>No Bonds</strong>: Assuming that the proof system works properly, there are no possible dishonest actions to take, meaning bonds are not needed.</p></li></ul><p>There are two main validity systems at the moment: Zero-knowledge (ZK) and trusted execution environments (TEE).</p><h4 id="h-zk-proof-systems" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">ZK Proof Systems</h4><p>These are decentralized systems that prove the state of a chain through advanced mathematics and cryptography. There are also hybrid models that combine an optimistic and ZK approach, but time for challenges and bonds is still required, albeit with much lower bond requirements than the full optimistic setup. Examples of these are <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/succinctlabs/op-succinct">Op-Succinct</a> and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/boundless-xyz/kailua">Kailua</a>.</p><p>However, the main drawbacks preventing full adoption are:</p><ul><li><p><strong>Complexity:</strong> The offchain components rely on advanced mathematics and cryptography, which are difficult to soundly implement. Detailed specifications, proper theory-to-implementation practices, and formal methods are artifacts that give us confidence in a ZK prover.</p></li><li><p><strong>Cost: </strong>The resources needed to prove Base blocks in real-time are high, especially as we look to scale the chain further. Fortunately, there have been many advances in this field bringing costs to a likely manageable level in the future.</p></li></ul><h4 id="h-tee-proof-systems" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">TEE Proof Systems</h4><p>TEE proofs provide assurance for integrity from a hardware point-of-view. These proofs ensure that the state of Base was derived correctly and not tampered with. They are also efficient to produce and cheap to verify.&nbsp;</p><p>The main downside is that this is a centralized option. Trusting a TEE requires trusting an authority, such as AWS or Intel. TEEs can be further divided into physical TEEs and cloud-based ones, each with their own downsides:</p><ul><li><p>There are known attacks against TEEs if one has physical access, such as side-channel attacks</p></li><li><p>Cloud-based TEEs include another layer of trust: the cloud provider</p></li></ul><h2 id="h-our-solution-multiproofs-with-tee-and-zk" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Our Solution: Multiproofs with TEE and ZK</h2><p>The solution we landed on is a combination of TEE and ZK provers, a system inspired by Vitalik’s <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ethereum-magicians.org/t/a-simple-l2-security-and-finalization-roadmap/23309">L2 finalization roadmap</a>. At a high-level, the proof system:</p><ul><li><p>Requires either a TEE or ZK proof to finalize a proposal</p></li><li><p>Allows fast finalization (at most 1 day) if both TEE and ZK proofs are present</p></li><li><p>Allows a ZK proof to overrule a TEE proof</p></li><li><p>Raises a soundness alert when contradictory proofs exist</p></li></ul><p>We immediately obtain the following:</p><ul><li><p>Better liquidity efficiency due to fast finality when both TEE and ZK proofs are present</p></li><li><p>Decentralization as proposing remains permissionless with ZK proofs and permissionless components override permissioned ones</p></li><li><p>Technical requirements for Stage 2 since we can now detect proof system bugs onchain</p></li></ul><p>We view ZK proving as the ideal setup for the future. However, before we can get there, we need to build our confidence and knowledge of this technology in order for us to fully rely on it later. That is why as an intermediary step, we view TEE proofs as the main way to progress the proof system while maintaining safeguards as we gain confidence in ZK technology.</p><h3 id="h-security" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Security</h3><p>As the proof system is what secures Base, it was the main focal point in the design process. By shifting from an optimistic system to a validity one (a proof is required for every proposal), we raise the assurance that each claim is correct.</p><p>The main worry now is if there is a vulnerability in a prover and an incorrect proof can be produced. We address this with the following added functionalities:</p><ul><li><p><strong>Soundness Alerts</strong>: If contradictory proofs exist, such as two ZK proofs for the same block number with different claims, then the proof system automatically disables the associated prover.</p></li><li><p><strong>Intermediary Roots</strong>: While proving a proposal requires proving every block the proposal covers, showing a contradiction only requires showing disagreement with one block. This is where intermediary roots come in, which are subclaims every couple of blocks in a proposal. Showing a contradiction with a subclaim is enough to contradict the entire claim, raising a soundness alert. As a result, defenders do not have to disprove an entire proposal, but only a small portion, reducing the requirements on defender resources.</p></li></ul><p>Our choice of SP1 as the current ZK prover reflects this, as it is one of the most performant and secure prover on the market right now, backed by several audits and formal verification.</p><h2 id="h-future-roadmap" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Future Roadmap</h2><p>The new proof system was designed with a particular end state in mind: full ZK proving. This will allow us to have near instantaneous withdrawals, a feature we believe will be great for users.</p><p>To get there, we will be exploring ways to:</p><ul><li><p><strong>Improve ZK Security and Efficiency: </strong>We are looking at changes we can make to the chain to make real-time proving a reality and we are always evaluating various ZKVMs to see how this technology is improving.</p></li></ul><ul><li><p><strong>Decreasing Finality Times</strong>: As confidence and efficiency in ZK improves, we will be looking to decrease the time it takes a proposal to finalize, ideally to just a couple of minutes.</p></li></ul><p>This next chapter in our journey towards full decentralization on Base is defined by the launch of the multiproof system, an architecture that secures our network while laying the groundwork for improved&nbsp; user experience and liquidity efficiency via decreased withdrawal times. By leveraging the complementary strengths of TEE and ZK proofs, we have not only met the technical requirements for Stage 2 decentralization but have also established a robust framework for future innovation. As we continue to evaluate ZK technology and work towards decreasing finality times, we are dedicated to realizing our ultimate goal: a credibly neutral, secure, and performant L2 that is ready to scale the onchain economy to a billion users.</p>]]></content:encoded>
            <author>base-engineering-blog@newsletter.paragraph.com (Roger Bai)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/09c177c50ec6ab1232051a6fba69364098e5060367a0ff8ed496a0d5b7a5019d.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Introducing Base Azul]]></title>
            <link>https://blog.base.dev/introducing-base-azul</link>
            <guid>jbzghxF4nZhRDPk3XcSa</guid>
            <pubDate>Tue, 21 Apr 2026 17:00:01 GMT</pubDate>
            <description><![CDATA[Base Azul, our first independent upgrade, is live  on Base Sepolia. Read on to learn how Azul makes Base more secure, more performant, and easier to build on.]]></description>
            <content:encoded><![CDATA[<p><strong>tl;dr Base Azul, our first independent network upgrade, is targeting mainnet activation on May 13, 2026. Azul makes Base more secure, more performant, and easier to build on.</strong></p><p>In our <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.base.dev/next-chapter-for-base-chain-1"><u>previous post</u></a>, we shared why Base is consolidating onto a streamlined Base stack to ship faster and simplify the protocol. Base Azul is the first network upgrade built on top of this new foundation. Base Azul is now live on testnet and is scheduled for Base Mainnet on May 13.</p><p>The Azul upgrade follows months of work for improved stability and consolidating our stack. And it's already paying off. Over the past two months, we've:</p><ul><li><p><strong>Increased Builder Reliability</strong>: Reduced the number of empty blocks by ~99% — from ~200/day to ~2/day</p></li><li><p><strong>Increased Chain Throughput</strong>: Sustained multiple 5,000 TPS bursts</p></li><li><p><strong>Shipped Faster</strong>: Cut client releases fortnightly, added new features, and improved validator performance</p></li></ul><p>Security is our top priority for Base. Every onchain component and proof system in Azul has gone through both internal and external audits. And to get as many eyes on the code as possible before activation, we're running an <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://immunefi.com/audit-competition/audit-comp-base-azul/"><u>Immunefi audit competition</u></a> from April 21 to May 4, with a $250,000 maximum reward pool for any critical vulnerabilities reported.</p><h1 id="h-whats-in-azul" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">What’s in Azul?</h1><p>Azul delivers impact in three key areas:</p><ol><li><p><strong>Increases security and decentralization</strong>: Multiproof-driven Stage 2 decentralization</p></li><li><p><strong>Accelerates the path to 1 gigagas/s</strong>: Performance-focused client stack consolidation</p></li><li><p><strong>Improves developer experience</strong>: Ethereum Osaka spec alignment</p></li></ol><h3 id="h-increased-security-and-decentralization" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Increased security and decentralization</h3><p>With Azul, we're activating multiproofs on Base - a major step in our path toward Stage 2 decentralization. Multiproofs combine TEE and ZK provers into a single system: either proof type can finalize a proposal on its own, but when both agree, withdrawals finalize in as little as one day.</p><p>This unlocks faster withdrawals and better capital efficiency for users, while satisfying a core technical requirement of Stage 2: the ability to detect and handle proof system bugs onchain. Posting ZK proofs is permissionless and will override the permissioned TEE proofs if there is a contradiction. The design is inspired by <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ethereum-magicians.org/t/a-simple-l2-security-and-finalization-roadmap/23309"><u>Vitalik's L2 finalization roadmap</u></a> and gives us security-in-depth. An attacker would need to compromise multiple independent systems for fast withdrawals, not just one.</p><p>Multiproofs are an intermediary step towards our desired end state of full ZK proving with near-instant withdrawals. Getting there means onboarding additional ZKVMs, investing in real-time proving performance, and progressively shortening finality times as our confidence in the technology grows.</p><p>Over the next few weeks, we’ll be sharing a blog post that provides more details. For now you can learn more in our <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://specs.base.org/upgrades/azul/proofs"><u>specification</u></a>.</p><h3 id="h-accelerated-the-path-to-1-gigagass" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Accelerated the path to 1 gigagas/s</h3><p>Azul consolidates Base onto a single, performance-focused client stack:</p><ul><li><p>base-reth-node becomes our sole execution client. Reth has consistently been one of the highest-performing clients on Ethereum, and is providing the headroom to scale Base.</p></li><li><p>base-consensus is our new consensus client, based on Kona. It already delivers significantly faster historical sync, and we'll keep optimizing it on the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.base.dev/scaling-base-doubling-capacity-in-30-days"><u>path to 1 gigagas/s</u></a>.</p></li></ul><p><br>Azul drops support for all consensus and execution clients except for base-reth-node and base-consensus. You can get the latest client releases from <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/base/releases/latest"><u>base/base</u></a>.</p><figure float="none" data-type="figure" class="img-center"><img src="https://storage.googleapis.com/papyrus_images/e2ffd5080624d68626c762f009adc9eb394522510ffd93a249de662e4a4d49e0.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAALCAIAAACRcxhWAAAACXBIWXMAAAsTAAALEwEAmpwYAAACqklEQVR4nLWT70sTcRzH758QQh+IwXBWjJJNWfk7a6hzw1KnlFJaFOViKf7YMmVmpiURlZAz/fqgQTpGCMJ5ym2y3XR6E3dTd/bkJuyuJ6eR38DdE3ehV2LlAx/U69Hn83nD9/N5f7+fLyIeG0EQeJ4/UoIQbm5uHSkhfxwhiiIAICEhQYoPsxOLURQlxYIgaLVagiCklOM4hmGk2Gq1pqenQwilFIEQLi4uLAUCKyFqJRSa8xEURTEMM+/3z/v91OrqcjA46/HQNA0hXAuHIYRzPmIlROE4vhQIzPmIjUiE474wDLMRicy63dMYhmHorNtNkiSEEHE4HMX6SnWORq7ISJGd0VXUNhhNM/jMlZKSnvZ2hUyWr1Z3tbZeNxiiLMtxnMPhzC0sUqpzz6qyZKfOXSoua2pu3d7+RtPhG3W3H7Q8vnXPVFCk7+p9pS+vttvtCIqizS2PbtbfVWVeKC29arZYn/Y88y/4W0wmu82WlZFRrtXabbYOsznKslGWdblc941NdxoeKtXZZRXXzB3dL/pf8jxPkuTg4JB97FOJvvxEUnLnk+cfPjonJiYQp9NZV1XZa7HIkhJ1+QXWpsZGo9Hj9Rp0uu62ttMpKTlKZY/ZXF9bKzmYmsLO514+KVfIFarEZNnForKOzq59BzRBeN++eZ2TnZUmT62uMki3tOcAnZwszMtD9nk3MDAKAEmS74eGMlUqqdjf1zcKwPr6Z8nB8AhITU2TpOGRUQCA9Mi78fhOLLYbj4uiuBuPQwgFQUCiLIuiKEEQLpcLRdHlYHAtHP66tbUcDHq83r32v4oQQskERVHzPh+KojiO03SYpmme5zmOk7brgJ9bNI1hCIIAAMT/A0LTdE1NjUajIUnyb/nwLMeXfmsA4fex8XGLxXLwif4tPwCdxyVqCNXNDAAAAABJRU5ErkJggg==" nextheight="606" nextwidth="1712" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>We plan to further simplify the node software by combining these into a single binary base in the upcoming months.</p><h3 id="h-improve-developer-experience" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Improve developer experience</h3><p>Azul aligns Base with Ethereum by adopting the latest execution-layer specs (Osaka). The main changes are:<br></p><table><colgroup><col><col><col></colgroup><tbody><tr><th colspan="1" rowspan="1"><p><strong>Change</strong></p></th><td colspan="1" rowspan="1"><p><strong>What</strong></p></td><td colspan="1" rowspan="1"><p><strong>Why</strong></p></td></tr><tr><th colspan="1" rowspan="1"><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://specs.base.org/upgrades/azul/exec-engine#transaction-gas-limit-cap"><u>EIP-7825: Transaction Gas Limit Cap</u></a></p></th><td colspan="1" rowspan="1"><p>Introduces a per-transaction gas cap of ~17 million gas.</p></td><td colspan="1" rowspan="1"><p>Unlocks future validator performance.</p></td></tr><tr><th colspan="1" rowspan="1"><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://specs.base.org/upgrades/azul/exec-engine#clz-opcode"><u>EIP-7939: CLZ Opcode</u></a></p></th><td colspan="1" rowspan="1"><p>A new opcode to count the number of leading zeros.</p></td><td colspan="1" rowspan="1"><p>More efficient compute for smart contracts.</p></td></tr><tr><th colspan="1" rowspan="1"><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://specs.base.org/upgrades/azul/exec-engine#secp256r1-precompile-gas-cost"><u>Raise the cost of the secp256r1 precompile</u></a></p></th><td colspan="1" rowspan="1"><p>Raise the cost of the secp256r1 P-256 signature verification precompile to make Base match Ethereum.</p></td><td colspan="1" rowspan="1"><p>Consistency with Ethereum.</p></td></tr><tr><th colspan="1" rowspan="1"><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://specs.base.org/upgrades/azul/exec-engine#modexp-gas-cost-increase"><u>EIP-7883: MODEXP Gas Cost Increase</u></a></p></th><td colspan="1" rowspan="1"><p>Raises MODEXP gas costs to reflect actual compute cost.</p></td><td colspan="1" rowspan="1"><p>Consistency with Ethereum &amp; reduces DoS vectors.</p></td></tr><tr><th colspan="1" rowspan="1"><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://specs.base.org/upgrades/azul/exec-engine#upper-bound-modexp"><u>EIP-7823: Upper-Bound MODEXP</u></a></p></th><td colspan="1" rowspan="1"><p>Caps MODEXP input sizes so gas pricing stays predictable.&nbsp;</p></td><td colspan="1" rowspan="1"><p>Consistency with Ethereum &amp; reduces DoS vectors.</p></td></tr><tr><th colspan="1" rowspan="1"><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://specs.base.org/upgrades/azul/exec-engine#remove-account-balances--receipts"><u>Flashblocks: Decrease Flashblocks payload size</u></a>.</p></th><td colspan="1" rowspan="1"><p>Breaking change to the Flashblocks websocket payload, removing account balances and receipts.</p></td><td colspan="1" rowspan="1"><p>Make the payload smaller, freeing up room for <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blockaccesslist.xyz/"><u>block access lists</u></a> and other performance hints in the future.</p></td></tr></tbody></table><hr><h1 id="h-how-azul-impacts-you" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">How Azul impacts you</h1><h3 id="h-users" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Users</h3><p>Users won’t have to take any action for this upgrade. Base just gets faster, safer, and cheaper for you to use. Withdrawals from Base to Ethereum will get faster as the multiproof system matures.</p><h3 id="h-node-operators" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Node operators</h3><p>You'll need to migrate to the new Base clients before the network upgrade activates. For the consensus client, you’ll need to run&nbsp; base-consensus and base-reth-node for the execution client. Step-by-step instructions are in the node operator <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.base.org/base-chain/node-operators/base-v1-upgrade"><u>upgrade guide</u></a>.</p><p>If you serve historical proof RPCs (e.g. <code>eth_getProof</code>, <code>debug_executionWitness</code> and <code>debug_executePayload</code>) you’ll need to enable our historical proofs extension to serve these performantly. More information can be found <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.base.org/base-chain/node-operators/run-a-base-node#enable-historical-proofs-rpcs"><u>here</u></a>.&nbsp;</p><h3 id="h-developers" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Developers</h3><p>Most apps require no changes. If you use MODEXP heavily, send very large transactions, or consume the Flashblocks websocket directly, review the spec changes linked above before activation.</p><h3 id="h-auditors" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Auditors</h3><p>For white hats and security researchers, we’re running an audit competition on the testnet code with Immuefi with a reward pool. More information can be found <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://immunefi.com/audit-competition/audit-comp-base-azul/"><u>here</u></a>.&nbsp;</p><hr><h1 id="h-whats-next" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">What's next</h1><p>Azul is the first of many Base upgrades. In the spirit of building in public, we are sharing our current working plan for the next several months of network upgrades. We will continue to iterate based on community feedback and the evolution of Base.<br><br>We expect the next two upgrades to include:&nbsp;</p><ul><li><p><strong>&nbsp;End of June</strong>: Performance-focused. Enshrined token standard, Flashblock Access Lists, Glamsterdam EIPs, single client binary and reduced withdrawal times.</p></li></ul><ul><li><p><strong>End of August</strong>: UX-focused. Ship native account abstraction.</p></li></ul><p>In mid-May we're also launching Base Vibenet, a public devnet for developers to experiment with our upcoming features and give us early feedback. Vibenet isn't tied to a hardfork, it's our permanent space to try things ahead of mainnet.</p><p>Azul is the first network upgrade on a stack we control end-to-end, and the start of a steady cadence of independent upgrades on the path to a global, free, onchain economy that serves the next billion users.</p><p>If there's something we can do better, we want to hear it. Reach us on <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/buildonbase"><u>X</u></a>, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://discord.com/invite/buildonbase"><u>Discord</u></a>, or <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/base/issues"><u>GitHub</u></a>.</p><br>]]></content:encoded>
            <author>base-engineering-blog@newsletter.paragraph.com (Base Engineering Team)</author>
            <author>base-engineering-blog@newsletter.paragraph.com (Danyal Prout)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/aa6123125cf8ff7dba6dd726b875dc201d626be8278b79ad4c0801beccfacd0d.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[A new, unified stack for Base Chain]]></title>
            <link>https://blog.base.dev/next-chapter-for-base-chain-1</link>
            <guid>Sei1pO8j8DHvrCL3iU4N</guid>
            <pubDate>Wed, 18 Feb 2026 17:03:12 GMT</pubDate>
            <description><![CDATA[To accelerate innovation, scaling and security, Base is evolving its foundational software by moving to a unified, Base-operated stack. Once we complete this transition, node operators will need to follow releases from ⁠base/base instead of Optimism’s releases. We will communicate more as this date approaches.]]></description>
            <content:encoded><![CDATA[<p><strong>TL;DR: </strong>To accelerate innovation, scaling and security, Base is evolving its foundational software by moving to a unified, Base-operated stack. Once we complete this transition, node operators will need to follow releases from <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/base"><u>⁠base/base</u></a> instead of Optimism’s releases. We will communicate more as this date approaches.</p><h2 id="h-the-evolution-of-the-stack" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Evolution of the Stack</h2><p>The mission Base launched with is the same mission we have today: make it the best place to build and onboard the next billion people into the onchain economy. We knew that this required creating a secure, low-cost, developer-friendly home for anyone, anywhere, to build on top of. To get there quickly, Base launched as an OP Stack chain. Over time, we grew to incorporate a diverse range of software in collaboration with various partners, including Optimism, Flashbots, and Paradigm. These collaborations enabled features like <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.base.dev/accelerating-base-with-flashblocks"><u>Flashblocks</u></a>, but also introduced a complex web of external dependencies.</p><p>Today, we’re introducing our new <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/base"><u>unified stack</u></a> for running Base - where all dependencies and future innovations are now consolidated into a single place. Base was built on the shoulders of giants - we could not have gotten so far so quickly without the world-class technology underpinning the OP Stack and are grateful for the collaboration over the last 3 years.</p><h2 id="h-why-are-we-doing-this" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Why are we doing this?</h2><p>We have three objectives:</p><ul><li><p><strong>Faster Shipping Cadence: </strong>Consolidating the stack means we can move faster. We're targeting six smaller, tightly scoped hard forks per year, doubling the current schedule (vs three per year currently). We’ll ship upgrades frequently rather than batching risk into infrequent large ones.</p></li><li><p><strong>Reducing Cognitive Overhead: </strong>The protocol spec and codebase should be understandable by a single developer. By focusing on just what Base needs, we’ll radically simplify the stack.&nbsp;</p></li><li><p><strong>Collaborative success with Ethereum:</strong> Base wins when Ethereum wins. We will accelerate to deploy high-impact changes, such as <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-7928"><u>BAL</u></a>, ahead of the L1 to provide data that informs the Ethereum roadmap.</p></li></ul><h4 id="h-a-unified-stack" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">A Unified Stack</h4><p>Today, the code operating various components of Base, such as the sequencer, is owned by multiple teams and spread across multiple repositories, which adds coordination and maintenance overhead. Our unified solution, base/base, built on open-sourced components such as Reth, allows us to dramatically simplify the number of components, by optimizing them directly for our use case.</p><br><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/2976097933931d3e7fd7e125f7c7a830948f073259e967683349770784284fec.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAATCAIAAAB+9pigAAAACXBIWXMAAAsTAAALEwEAmpwYAAADm0lEQVR4nLWUb2wTZRzHH14YNcayGBarMPYnLGyTOcHKH5u+KC6OtOlI3QbVrlu3UWKBlTWlpcWxzsHKFQzd0PVJWNyVZKUquadKuprw4vDFeV1YdqkZHCQsz1wChy/YjXfFN5xpu2j0bqCJfHIvLr97cp/n+T6/5wHSXzyRpCe5AoV3ZR4uLRUG/FuAYlUUxbING3iel3/iOE4u0Gq1KpVKcfyKIJfLUXmufJ9EFHVlYuJrCKHiTDOZjLxO0zRCSBRFZQHGeIK8tFWz8cSA6aUXQYPmlR6HznX0yMLCovxfM7Oz/zkijuN+mZs7YO88HQxs3VJrbTN7XM5odIzjOPmkVhOsZs0LeJ6/O3+3p6MzPBAEAOje2+7vPQohxHhBLihGxPM8ScYoClEUmownEolvBEGQJGl5+dG9+8KDB789XFrCeGF5+VFeMDM7izFGCLEsW15RPhwa5jguEom0tLZCCOUrEEXR2t6+U/fB+qoaAIBGq28y7h0cDLIsezI4WPfOTgDAmpdfU5dtupz4diUiQRAghGNRmE7/OBmPh84Q6XQ6lUphjP/s3WL73rx1K5lM9rndzl73uzu069TrTeZ9vX2ekZFRnuej8KK109HYZDSYzB/tb7+aSgGO42w2G0EQ9dt2VdZqQJ4XtuuaxqIQYyyPiOd5ggh3dXe3Wmyl6rI3N1Ztqn3bYrV7PV6E0Mng4MCpsy37bS0We5+3n6KSACFUWrquoWHbnj06zzFrRdVag3GH83Bbc7OJJGM0TY+Ph1wui8/X1d9/gKLGM5kbGOPjfn+7/WDbx/bK6rf6vP0Hnb0/Xb8uSdLvjx//48lHNHdzLnQmvOv9uuP+fSVrQeOHNYcON3d3dzE/sxRFfXrIXF6xpnqzanPdq+Gzx4qbTJLkyFfQYDIDAM6dvxA+9wVN04q9lBcQBMEwTIflkyGf/3VViXF345GuHofDQRbosdqGfP6G6pohnz/g9jAMk8vlMMZ3bvPTGfZSLHbnNp/NZhVP2YoAQsiyrM/tvvjlqNflOj8c+vyzExdGRyVJEgRh6uoP16ZS312evDaVmmYYxZP8FPKCxV8XJ+PxvQZDm9H4RklJpVrt7OiMRCKKS878XfBM2cpdJIoiSZKBQCDf1xpNIpFIp9PyTZNWueyeISBJUq/X4wLZbHZ+fl6xQYusVn+a4PSpIQBAMZP/HSBJ0o2Z6fr6LXr97ucluHdfIMkYQuh5CP4AvtCrZL0K+xcAAAAASUVORK5CYII=" nextheight="2149" nextwidth="3662" class="image-node embed"><figcaption htmlattributes="[object Object]" class="">Base Sequencer architecture before and after</figcaption></figure><p>Consolidating into base/base changes how Base packages and releases software for the network. We will ship <strong>one official distribution</strong> for each upgrade: a single Base binary for operating nodes on the network.</p><p>This unification does <strong>not</strong> mean Base will be built in isolation. The protocol remains public and specified in the open, and alternative implementations are welcome and encouraged. Any team can build, run, and maintain an independent client that follows the published specs and remains compatible across hard forks.</p><h4 id="h-accelerating-upgrades" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Accelerating Upgrades</h4><p>Evolving the stack means updating Base’s software and upgrade process. Over the coming months, Base is moving away from the OP Stack, however, we’ll continue to work with Optimism as a client of OP Enterprise: Mission-Critical Support. Once the transition is complete, upgrades and release information will be published through ⁠base/base.&nbsp;</p><h4 id="h-maintaining-high-decentralization-guarantees" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Maintaining High Decentralization Guarantees</h4><p>It was important that in this evolution, we didn’t make any tradeoffs in security or technical decentralization. We are committed to maintaining, and exceeding, the highest standards for rollup safety.</p><ul><li><p><strong>Stage 1 Status:</strong> Base remains a<strong> </strong>Stage 1 Decentralized Rollup.</p></li><li><p><strong>The Base Security Council:</strong>&nbsp; In lieu of Optimism, we are adding an additional independent signer to the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.base.org/base-chain/security/security-council"><u>Base Security Council.</u></a></p></li><li><p><strong>Accelerated Security Roadmap:</strong> This also allows us to move faster on our decentralization milestones, including faster withdrawals via a more robust multi-proof system, Base-specific governance, and enhanced neutrality standards that protect economic autonomy across the entire stack.</p></li></ul><h4 id="h-building-in-public" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Building in Public</h4><p>We believe the technology behind Base should be a public good.</p><ul><li><p><strong>Open Source Forever:</strong> Base specifications and code will always be public, open for contribution, and available for others to fork.</p></li><li><p><strong>Client Diversity:</strong> We actively encourage the community to build alternative clients and implementations for Base to further harden the network.</p></li><li><p><strong>Ecosystem Tooling:</strong> We will continue to contribute to essential tooling like Foundry and Wagmi, ensuring a great developer experience for building on Base.</p></li></ul><p>Our goal is to assemble the best libraries from across the ecosystem and continually adopt better options as they emerge. We’re open to new partners and libraries that improve performance, security, or decentralization, and we’ll work in public and contribute upstream so the broader ecosystem benefits.</p><h2 id="h-how-does-this-impact-you" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">How does this impact you?</h2><p>Our goal is to ensure the transition to our consolidated stack is as seamless as possible for the builders, node operators, and users in the Base ecosystem. While the underlying technology is evolving, the experience of using Base will remain consistent. Here is what you can expect:</p><ul><li><p><strong>Today: </strong>No immediate action is required. Base will continue operating as it does today, and we will remain compatible with the OP Stack specification (including continued support for existing RPCs). We’ll continue to upstream bug fixes and coordinate security disclosures to help keep the broader Superchain ecosystem safe.</p></li><li><p><strong>Next Few Months:</strong> If you run a Base node, you will need to migrate to our Base client to remain compatible with future hard forks. All RPCs, including those in the optimism namespace, will continue to be fully supported to prevent breaking existing integrations.</p></li><li><p><strong>Longer Term:</strong> As Base matures, the network will become significantly more decentralized and scalable, enabling higher throughput and lower costs. To maintain stability, breaking changes that require node operators and clients to upgrade will be rare, occurring only during scheduled hard forks and communicated well in advance to the community.</p></li></ul><h2 id="h-roadmap-preview" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Roadmap Preview</h2><p>In the spirit of building in public, we are sharing our current working plan for the next several months of hard forks. We will continue to iterate based on community feedback and the evolution of Base.<br></p><table style="min-width: 265px"><colgroup><col style="width: 240px"><col></colgroup><tbody><tr><td colspan="1" rowspan="1" colwidth="240"><p><strong>Release</strong></p></td><td colspan="1" rowspan="1"><p><strong>Planned Features &amp; Changes</strong></p></td></tr><tr><td colspan="1" rowspan="1" colwidth="240"><p>Base V0 (today)</p></td><td colspan="1" rowspan="1"><ul><li><p>Establishing Base repositories, deployments, and binaries.</p></li><li><p>Implementing Flashblock Access Lists<strong> </strong>for improved transaction speed.</p></li></ul></td></tr><tr><td colspan="1" rowspan="1" colwidth="240"><p>Base V1 (upcoming hardfork)</p></td><td colspan="1" rowspan="1"><ul><li><p><strong>Client Consolidation</strong>: Node operators will need to transition to running releases from <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/base"><u>⁠base/base</u></a> instead of Optimism’s releases.&nbsp;</p></li><li><p><strong>Proof Upgrade:</strong> Transitioning from Optimistic proofs to TEE/ZK proofs for faster finality.</p></li><li><p><strong>Fusaka Support:</strong> Implementing Ethereum’s Fusaka upgrades.</p></li></ul></td></tr><tr><td colspan="1" rowspan="1" colwidth="240"><p>Base V2 (future hardfork)</p></td><td colspan="1" rowspan="1"><ul><li><p><strong>Block Access Lists:</strong> Enabling more efficient block processing.</p></li><li><p><strong>New Transaction Types:</strong> Introducing protocol-level UX improvements for smoother user interactions.</p></li></ul></td></tr><tr><td colspan="1" rowspan="1" colwidth="240"><p>Base V3 (future hardfork)</p></td><td colspan="1" rowspan="1"><ul><li><p><strong>Glamsterdam Support:</strong> Aligning with Ethereum's next major L1 upgrade.</p></li><li><p><strong>Op-code Repricing:</strong> Adjusting costs to reflect modern hardware performance.</p></li><li><p><strong>Economic Changes:</strong> Refining base fee mechanics and network economics.</p></li></ul></td></tr></tbody></table><h2 id="h-the-road-ahead-scaling-for-a-global-economy" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Road Ahead: Scaling for a Global Economy</h2><p>Base is moving from a patchwork of dependencies to a single unified stack in base/base. This means node operators will need to migrate to the Base client and releases will run on a faster, more frequent schedule.</p><p>We continue to be committed to building in the open: Base’s codebase and protocol remain open-sourced and influenced by Ethereum’s roadmap. Base will continue to raise its decentralization and security bar while we ship updates more predictably and efficiently.</p><h2 id="h-join-us" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Join Us</h2><p>Building the next frontier of the internet is a collective effort. If you are passionate about scaling, securing, or decentralizing the future of Base, we want to hear from you. View our open roles and apply <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.base.org/jobs"><u>here</u></a>.</p>]]></content:encoded>
            <author>base-engineering-blog@newsletter.paragraph.com (Base Engineering Team)</author>
            <category>base</category>
            <category>scaling</category>
            <category>stack</category>
            <category>innovation</category>
            <category>chain</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/2f2b9e08c6047b2088415cd83a1315282b46623fb5744543eaae94902972dbfa.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Builder Codes & ERC-8021: Fixing Onchain Attribution]]></title>
            <link>https://blog.base.dev/builder-codes-and-erc-8021-fixing-onchain-attribution</link>
            <guid>0jNXg5zWghrkLBz0BE0r</guid>
            <pubDate>Thu, 12 Feb 2026 21:03:18 GMT</pubDate>
            <description><![CDATA[TL;DR: Base is building a global onchain flywheel: builders create apps, apps drive transacting users, and transacting users expand the market to attract more builders. To accelerate this cycle, we need to measure and reward the apps that generate real value. ERC-8021 and Builder Codes provide this missing layer: a system for apps to prove their impact onchain and receive credit for the transactions they generate.The Attribution GapWhile anyone can link a transaction to a specific protocol, d...]]></description>
            <content:encoded><![CDATA[<p>TL;DR: Base is building a global onchain flywheel: builders create apps, apps drive transacting users, and transacting users expand the market to attract more builders. To accelerate this cycle, we need to measure and reward the apps that generate real value. ERC-8021 and Builder Codes provide this missing layer: a system for apps to prove their impact onchain and receive credit for the transactions they generate.</p><h2 id="h-the-attribution-gap" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Attribution Gap</h2><p><strong>While anyone can link a transaction to a specific protocol, determining which app facilitated the transaction is difficult.</strong></p><p>Protocols are onchain smart contracts users interact with directly. For example: decentralized exchanges like Aerodrome or lending protocols like Morpho.</p><p>Apps are offchain entities that drive transactions for any use case. For example: web and mobile interfaces, trading bots, and backend automations.</p><p>Protocols are straightforward to track because all interaction data exists publicly onchain. Apps however have no native link to the transactions they facilitate.</p><p>This gap has real consequences. Without attribution data, chains can't measure which apps bring value to the ecosystem. Allocation of attention, distribution, and capital investments becomes manual guesswork rather than data-driven decision making. Builder Codes and ERC-8021 close this attribution gap and enable builders to compete for resources in a way that’s more open and transparent.</p><h2 id="h-industry-inspiration" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Industry Inspiration</h2><p><strong>Others have independently recognized and addressed this problem in their ecosystems, but not in a way reusable by everyone.</strong></p><p>Hyperliquid pioneered the term "Builder Codes" by allowing apps to annotate transactions to receive a portion of generated swap fees, creating measurable incentive alignment and rewarding the apps that drive real volume.</p><p>Polymarket followed with a similar implementation where apps can annotate transactions through their order book API to qualify for rewards.</p><p>Both solutions work well within their ecosystems, however they are isolated to specific use cases (Hyperliquid for perpetuals, Polymarket for prediction markets). To make transaction attribution work across Ethereum, we needed a general-purpose standard that any protocol can adopt.</p><h2 id="h-erc-8021-transaction-attribution-for-ethereum" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">ERC-8021: Transaction Attribution for Ethereum</h2><p>We wrote <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eip.tools/eip/8021">ERC-8021</a> to enable attributing <em>any</em> transaction on Ethereum <em>today</em>. To do this, we had to work under a couple constraints:</p><ol><li><p><strong>It needs to work with Ethereum’s existing transaction format.</strong> This enables shipping quickly for both nodes and apps by staying interoperable with existing code and tools.</p></li><li><p><strong>It needs to work regardless of what transactions are trying to do.</strong> This enables us to serve all protocols with no integration effort and build developer network effects.</p></li></ol><h3 id="h-data-suffixes" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Data Suffixes</h3><p>To meet these constraints, we leverage an underutilized EVM property: smart contracts ignore extra data appended beyond expected function arguments. If you call a smart contract function with any additional data after function arguments, by default they are safely discarded without errors or reverts. Because this “data suffix” passes through without impacting execution, we can attribute safely and for any transaction type.</p><p>Here's what this looks like in practice:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/736a251a10c14b64c95b837785c5da5f958a2fdcba74d047fc61c2d70167cd0d.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAKCAIAAABaL8vzAAAACXBIWXMAAAsTAAALEwEAmpwYAAACDElEQVR4nGP4+vXr69evP4DB169f79y5A2dDwH/KAMORI0diYmK8vLzMzc3t7OzCw8P9/HyzMjOPHDk8fdq03bt3/v////u3b39+/0ZDcCNg3H+osv+gFvz////Ro0cpKSm7d+++fv26k5PT2vXrv/74eebs2efPX1DofKgFHz58yM3NnT9//qePH2vr6qf0tB3dtv74rg3XTx86vWfj6T0bT+xYCydP79l4fPua62cOw424e+X0uQPbn969fv30oVsXjl0/feDikV3vXj5DscDc3NzY2Li8rOzd+08Luypqom2ro226c4NzvbTLw0xKgvUrwkxLgvVLQwzLw4xzPFWn1yTCLZhYEtGY5LR2ekt9vPPEkoiaaNuKcLMrx3dDQg9kwf///3/9+gXX8PDGhVO7166a0tCU5NaQ4M4r7sDAoM3AYw4j9RgYDKWUPB88uAvW+JtPwpGBQd7cIs7SOoGBQYeBQY2B1WTXrhMQWZAFv379egYG169fv33nzt1bV7evW75l+by1c3oXTe6oKJ+ckdlWWNgLIXNyu3Jyu/r6lsId1Ne3tLRs4uzZ66dPX9vYNHvt2n1dXYvu3H6MCKKvX7+ePHnyzp07ixcv3rhx46VLl46fOLVq9drNW7atWr3227dvFEXykSNH1q5dixZKMABKar9+/caK4IrwyzJAMhrEAkw7sNlKmg8AbM4LpqGgizYAAAAASUVORK5CYII=" nextheight="354" nextwidth="1140" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>A smart contract only reads up through abi-encoded data (<code>0xabcd...1234</code>) to execute the interaction. The attribution data suffix (<code>0xaaaaaaaaaaaa</code>) passes through without affecting execution, but remains available in transaction data for analytics.</p><p>This pattern isn't new. Many teams already use it in production at scale including Aerodrome, Morpho and the Base App.</p><p>What's been missing is standardization. Each team implements their own format, making it difficult to parse attribution across different apps. <strong>ERC-8021 solves this by defining one canonical data suffix. The standard supports both regular transactions and ERC-4337 user operations, ensuring compatibility with all wallet types.</strong></p><h2 id="h-standardizing-the-data-suffix-schema" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Standardizing the Data Suffix Schema</h2><p>ERC-8021 builds up its schema by solving three core problems: interoperability, extensibility, and identity. Let's derive the format from first principles.</p><p><strong>Problem 1: Interoperability</strong></p><p>Existing attribution methods struggled with interoperable detection. While a specific app can append a custom data suffix they know to look for, how does a separate party discern if transaction data ends with attribution or not? Without a standard marker, every parser needs custom logic for every attribution format.</p><p>ERC-8021 solves this with a constant marker at the end of every data suffix: <code>0x80218021802180218021802180218021</code> (16 bytes of "8021" repeated). This enables parsers to reliably detect attribution is present by checking if the last 16 bytes match the standard marker. The probability of accidental collision with this specific 16-byte sequence is negligibly low (1 in 2^128), making it a reliable detection mechanism.</p><pre data-type="codeBlock" text="ercMarker = 0x80218021802180218021802180218021
attributionData = 0xaaaaaaaaaaaa
dataSuffix = attributionData + ercMarker
           = 0xaaaaaaaaaaaa80218021802180218021802180218021"><code><span class="hljs-attr">ercMarker</span> = <span class="hljs-number">0</span>x80218021802180218021802180218021
<span class="hljs-attr">attributionData</span> = <span class="hljs-number">0</span>xaaaaaaaaaaaa
<span class="hljs-attr">dataSuffix</span> = attributionData + ercMarker
           = 0xaaaaaaaaaaaa80218021802180218021802180218021</code></pre><p><strong>Problem 2: Extensibility</strong></p><p>Attribution needs will evolve. Today's format might not serve tomorrow's use cases for new attribution types, more efficient encoding, additional metadata fields, and more. We needed a versioning mechanism to iterate without breaking existing implementations.</p><p>ERC-8021 adds a one byte schemaId immediately before the ercMarker to identify how to parse additional preceding schemaData. This single byte provides 256 possible schema versions, each defining how to parse the actual attribution data.</p><pre data-type="codeBlock" text="ercMarker = 0x80218021802180218021802180218021
schemaId = 0x00
schemaData = 0xaaaaaaaaaaaa
dataSuffix = schemaData + schemaId + ercMarker
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 0xaaaaaaaaaaaa0080218021802180218021802180218021"><code><span class="hljs-attr">ercMarker</span> = <span class="hljs-number">0</span>x80218021802180218021802180218021
<span class="hljs-attr">schemaId</span> = <span class="hljs-number">0</span>x00
<span class="hljs-attr">schemaData</span> = <span class="hljs-number">0</span>xaaaaaaaaaaaa
<span class="hljs-attr">dataSuffix</span> = schemaData + schemaId + ercMarker
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 0xaaaaaaaaaaaa0080218021802180218021802180218021</code></pre><p>The suffix is designed to be parsed backwards from the end of calldata:</p><ol><li><p>Extract last 16 bytes → verify ercMarker matches standard</p></li><li><p>Extract previous byte → switch on schemaId to determine parsing rules</p></li><li><p>Parse remaining bytes according to schema specification</p></li></ol><p><strong>Problem 3: Identity</strong></p><p>For attribution to be useful, it needed to help identify who gets credit for a transaction. Existing candidates to identify this were Ethereum addresses, app names, and website domains, but each of them compromises on an important property. The ideal identifier is small and can link to additional information as needed.</p><p>ERC-8021 chooses "codes" as string-based identifiers (e.g. "abc123"). Codes are familiar to developers, mapping to concepts like referral codes in traditional apps, and are human-readable for debugging.&nbsp;</p><p>ERC-8021 also defines a Code Registry smart contract where codes can be registered and their additional related information read from, generally splitting into either onchain or offchain metadata.</p><p><strong>Onchain metadata</strong> enables protocol rewards. Protocols can query where rewards credited to a code should be sent to. For example, a DEX could reward the frontends generating the most trades.</p><p><strong>Offchain metadata</strong> enables app discovery. By associating codes with app names and domains, we create a transparent view of the ecosystem. Public dashboards can surface top apps by transaction volume, turning onchain usage data into a distribution channel for users to find new apps.</p><p>Anyone can deploy their own Code Registry so long as it adheres to the standardized interface. Different ecosystems can run their own registries with custom rules while still enabling others to parse attribution and query related information.</p><pre data-type="codeBlock" text="interface ICodeRegistry {
&nbsp;&nbsp;&nbsp;&nbsp;/// @notice Returns the address where protocols should send rewards
&nbsp;&nbsp;&nbsp;&nbsp;/// @dev This enables anyone to distribute rewards to an app that drove volume
&nbsp;&nbsp;&nbsp;&nbsp;    function payoutAddress(string memory code) external view returns (address);

&nbsp;&nbsp;&nbsp;&nbsp;/// @notice Returns a URI to fetch offchain metadata (app name, website, logo)
    /// @dev This enables building discovery interfaces and leaderboards
&nbsp;&nbsp;&nbsp;&nbsp;function codeURI(string memory code) external view returns (string memory);

&nbsp;&nbsp;&nbsp;&nbsp;/// @notice Checks if a code follows format rules (character set, length constraints)
&nbsp;&nbsp;&nbsp;&nbsp;/// @dev Parsers use this to filter malformed data
&nbsp;&nbsp;&nbsp;&nbsp;function isValidCode(string memory code) external view returns (bool);

&nbsp;&nbsp;&nbsp;&nbsp;/// @notice Verifies a code has been registered
&nbsp;&nbsp;&nbsp; function isRegistered(string memory code) external view returns (bool);
}"><code><span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">ICodeRegistry</span> </span>{
&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment">/// @notice Returns the address where protocols should send rewards</span>
&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment">/// @dev This enables anyone to distribute rewards to an app that drove volume</span>
&nbsp;&nbsp;&nbsp;&nbsp;    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">payoutAddress</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> code</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">address</span></span>)</span>;

&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment">/// @notice Returns a URI to fetch offchain metadata (app name, website, logo)</span>
    <span class="hljs-comment">/// @dev This enables building discovery interfaces and leaderboards</span>
&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">codeURI</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> code</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span></span>)</span>;

&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment">/// @notice Checks if a code follows format rules (character set, length constraints)</span>
&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment">/// @dev Parsers use this to filter malformed data</span>
&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">isValidCode</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> code</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>)</span>;

&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment">/// @notice Verifies a code has been registered</span>
&nbsp;&nbsp;&nbsp; <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">isRegistered</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> code</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>)</span>;
}</code></pre><h3 id="h-end-to-end-flow" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">End-to-End Flow</h3><p>Let's walk through how ERC-8021 works in practice, from registration through transaction submission and parsing.</p><p><strong>Step 1: Register a Code</strong></p><p>An app registers a code (e.g., "baseapp") with a unique Code Registry. The registry stores onchain metadata and a pointer to offchain metadata for the code.</p><p><strong>Step 2: Encode the Data Suffix</strong></p><p>When preparing a transaction, the first app injects their code into an ERC-8021 data suffix (real example below):</p><pre data-type="codeBlock" text="ercMarker = 0x80218021802180218021802180218021
schemaId = 0x00
codesLength = 7
codes = [&quot;baseapp&quot;]
schemaData = codes.join(&quot;,&quot;) + codesLength
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 0x6261736561707007
dataSuffix = schemaData + schemaId + ercMarker
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 0x62617365617070070080218021802180218021802180218021"><code>ercMarker <span class="hljs-operator">=</span> <span class="hljs-number">0x80218021802180218021802180218021</span>
schemaId <span class="hljs-operator">=</span> <span class="hljs-number">0x00</span>
codesLength <span class="hljs-operator">=</span> <span class="hljs-number">7</span>
codes <span class="hljs-operator">=</span> [<span class="hljs-string">"baseapp"</span>]
schemaData <span class="hljs-operator">=</span> codes.join(<span class="hljs-string">","</span>) <span class="hljs-operator">+</span> codesLength
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-operator">=</span> <span class="hljs-number">0x6261736561707007</span>
dataSuffix <span class="hljs-operator">=</span> schemaData <span class="hljs-operator">+</span> schemaId <span class="hljs-operator">+</span> ercMarker
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-operator">=</span> <span class="hljs-number">0x62617365617070070080218021802180218021802180218021</span></code></pre><p><strong>Step 3: Append Data Suffix to Transaction Data</strong></p><p>The app then appends this data suffix to the transaction data:</p><pre data-type="codeBlock" text="transaction.data = abiEncodedData + dataSuffix
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 0xabcd...1234 + 0x62617365617070070080218021802180218021802180218021
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 0xabcd...123462617365617070070080218021802180218021802180218021"><code>transaction.data <span class="hljs-operator">=</span> abiEncodedData <span class="hljs-operator">+</span> dataSuffix
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-operator">=</span> <span class="hljs-number">0xabcd</span>...1234 <span class="hljs-operator">+</span> <span class="hljs-number">0x62617365617070070080218021802180218021802180218021</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-operator">=</span> <span class="hljs-number">0xabcd</span>...123462617365617070070080218021802180218021802180218021</code></pre><p><strong>Step 4: Submit Transaction as Normally</strong></p><p>After the transaction is prepared, it can be signed and submitted by the app directly or through prompting the user to do so with their wallet.</p><p><strong>Step 5: Parse and Attribute</strong></p><p>After the transaction is confirmed onchain, analytics tools parse its data for attribution:</p><ol><li><p>Check last 16 bytes for <code>ercMarker</code> → confirms ERC-8021 format</p></li><li><p>Read <code>schemaId</code> byte → determines parsing rules</p></li><li><p>(Given schema 0 in this example) Extract codes length and codes array → <code>"baseapp"</code></p></li><li><p>Query registry for payout address and metadata matching <code>”baseapp”</code> code</p></li></ol><p>The result: universal, interoperable transaction attribution across Ethereum. Learn more by reading the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eip.tools/eip/8021">official specification</a>.</p><h2 id="h-base-builder-codes" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Base Builder Codes</h2><p><strong>Base aims for every transaction to be attributed with ERC-8021</strong> to inform how we prioritize our attention, distribution, investment capital, and rewards for the teams that grow Base's global economy.&nbsp;</p><p>To make this happen, we’re launching Base Builder Codes: the first ERC-8021 Code Registry. Apps that opt-in and append Base Builder Codes to their transactions can make it on our <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://base.dev/leaderboard">public leaderboard</a>. Anyone can claim a Base Builder Code through base.dev for free today.</p><p>Several key partners in the Base ecosystem have already agreed to implement ERC-8021 builder codes on Base to help close the attribution gap: Aerodrome, Avantis, Bitget, Clanker, Definitive, Flaunch, Hydrex, Jumper, Kyber, Limitless, Maestro, Mamo, Moonwell, o1, PancakeSwap, Privy, Rips, Sigma, Slab, Sport.Fun, Swissborg, Treble, Turnkey, Virtuals, and more.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e5ce4cc1a0b55cc4100156aee576dbd33c76ec9fde6d3275de6835cba58377bd.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAASCAIAAAC1qksFAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEkklEQVR4nLVV228UVRg/D75oFIgRU16JkT8BQ4jVN8QEGi/EG5dY0wfkwVtiJA0iJibQlFsURDBAU+WFtoisSRG2G8uyW7G7sCnbTafTbbdLZ2d35syZnZkzZ86c3c/MnKYi8dXv6cuZ7/79vt8gQkg+n1cUpVwuT05OqqoK/yVCCMYC6jPXo03HpZQyFjAWtB+y4TzknAecCyFWvBDnXNf1arVKCDEMA2OLc95qRY4ilocSMCFEwLlUwlgPOG//27jVavNYlhPA/ywIAIaHhzs61k1kJwBgfPzWZ59+XigUACCZHEskEtVqFQCOHz+xZcsrAEAIOXnyW0JsANA0LZFIOI4DAPl8PpVKyaD9/f2Dg4Px0HiUYO/eDxFCfX19APDe27sQQpnbWSFEb2/vvn37xsfHo/edOxGKjMMw7OzsPH/+PAAkEomurq5SqQQAa9c+Kw0cx+3u7h4dHQUASmn0xBgrFouMMQAwDKNcLku9VCrl83dlUYQQjLHUS6WSbEvTtKmpKUJsxthoLBhbhNjpdOZW+jaPthU8ugMhxMp+eIwHuTrq+4wFcoFyJrIyxpjtOE3HdT0vghml0tKJXujyknVdP336NCEEAKZLpQMHvrxx4yYApFKpwcGfZaxCoXAzOUYpBYBKpTI8PCIdLw4MuJ4XitbVq7+OXLkqcXz27LmRK7+0V1CUTqdXrVqTy+UAYOvWV59ctfqjjz8BgO1dr61f/1w8H/vgwa927d4zVy4DwPMbNmzc+AIAvN/9AUJIUdQ2wFvvvPv6G29Sn80o6uNPPPVi58tNxw04R0II6vs1ve56UX+E2BaxMbZMbC9UHsyqqoktizQxtmp6nfrMxPjy5aH7xSIAjKX+OHTo65quBzxMJseSybGm49X0xrHjJwYGf6I+i1BEfdYwzMLU1IMHS03HbRjm/eK0iW0T24qiziizml5vOu794nQud5exwPO8uXJ5SdNMbCmKouv6jDJrGOasqk6XSia2Goa5WK1WKosWsanPkEXs+fnKXHne9ahF7IXKYiaTrdeNpuNmMtlr136LO7DltuXCCSEWseU7pcvMsaRp8/MVxhj1/YXK4pKmWaRpYgsxFk78eQehx2bVOQB4Jobzkb5+AFi95mmEUC6X07TapUuXjh49lkqlqE8RQvt7DwDA92d+6OnpkeNCsQQxAnt6ei5cuCiXh8JQ3PlrcvPmzZ7nyW8IodHR3wFg1+49HR3rVHVO02pDQ0P5/F0J887Ol65fjwzS6cx3p04ZhgEA27Zt37Rpk2xxx44dhw8fWU7AWFBvNGZV1XHc+A4dVZ1rGCZjgabVKpVFSXyEkCVNC+MpYYxlNYSQcnle6oZh6rrebrWEENls9t69Qsy+PuI8ZCwwDNPzvPj2GMbYcVzqs6bjSmd5Uys6xlieuud5GGPXi6g7TlCPgBidGg1FCACu5/1zyW1oU0qj0JRyzmWxEfnEzCx1IYRs6BGJ6Tkq1HE8gNbIyL0v9n9z7sczhoHRIw6MBZxHyaWEYeQmrz+Io6yQ/sPsIn8+kio4DxYWcC4/NaMolNK/Adh3zd6AI/oRAAAAAElFTkSuQmCC" nextheight="1080" nextwidth="1920" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>For additional details on our smart contract implementation, read more at github.com/base/builder-codes.</p><h2 id="h-integrating-base-builder-codes" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Integrating Base Builder Codes</h2><p>Integrating Base Builder Codes impacts how you prepare transactions and potentially how you submit them, depending on the wallet stack your app uses.&nbsp;</p><p>There are typically three wallet types that apps can submit transactions from:</p><ol><li><p><strong>Universal wallets</strong>: Third party experiences brought by users (e.g. Base App, MetaMask)</p></li><li><p><strong>Embedded wallets</strong>: Provisioned by apps for each user (e.g. Privy, CDP Embedded Wallet)</p></li><li><p><strong>Server wallets</strong>: Managed by apps server-side independently (e.g. Turnkey, CDP Server Wallet)</p></li></ol><p><strong>If you're a wallet provider</strong>: You need to expose a way for apps to customize transaction data. For universal wallets, this likely means implementing ERC-5792's <code>wallet_sendCalls</code> with the <code>dataSuffix</code> capability. For embedded and server wallets, this likely means ensuring apps have a means to append manually to transactions via your APIs and SDKs. ERC-4337 accounts may require additional work to support data suffix appending for user operations. Read more <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.base.org/base-chain/builder-codes/wallet-developers">here</a>.</p><p><strong>If you're an app</strong>: The integration path depends on your wallet model. Apps with direct control over signing (embedded or server wallets) can manually append data suffixes. Apps using universal wallets rely on the <code>dataSuffix</code> capability in <code>wallet_sendCalls</code>. Read more <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.base.org/base-chain/builder-codes/app-developers">here</a>.</p><h3 id="h-frontend-integration-with-universal-wallets" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Frontend Integration with Universal Wallets</h3><p>These apps enable users to bring their own wallet to authenticate and use onchain funds. Users connect via web popups, browser extensions, mobile wallets, and more.</p><p>Apps submit transactions with Universal Wallets through standardized wallet APIs. Wallets can customize how they render these requests to users, facilitate signing, and transaction submission. We recommend using the <code>wallet_sendCalls</code> method with a <code>dataSuffix</code> capability:</p><pre data-type="codeBlock" text="// Sample ERC-8021 attribution  for &quot;baseapp&quot; from earlier
const response = await provider.request({
  method: 'wallet_sendCalls',
  params: [{
    calls: [{
      to: protocolAddress,
      data: encodedCalldata,
      value: '0x0'
    }],
    capabilities: {
      dataSuffix: {
        value: '0x62617365617070070080218021802180218021802180218021'
        optional: true
      }
    }
  }]
});"><code><span class="hljs-comment">// Sample ERC-8021 attribution  for "baseapp" from earlier</span>
<span class="hljs-keyword">const</span> <span class="hljs-variable constant_">response</span> = await provider.<span class="hljs-title function_ invoke__">request</span>({
  <span class="hljs-attr">method</span>: <span class="hljs-string">'wallet_sendCalls'</span>,
  <span class="hljs-attr">params</span>: [{
    <span class="hljs-attr">calls</span>: [{
      <span class="hljs-attr">to</span>: protocolAddress,
      <span class="hljs-attr">data</span>: encodedCalldata,
      <span class="hljs-attr">value</span>: <span class="hljs-string">'0x0'</span>
    }],
    <span class="hljs-attr">capabilities</span>: {
      <span class="hljs-attr">dataSuffix</span>: {
        <span class="hljs-attr">value</span>: <span class="hljs-string">'0x62617365617070070080218021802180218021802180218021'</span>
        <span class="hljs-attr">optional</span>: <span class="hljs-literal">true</span>
      }
    }
  }]
});</code></pre><p>We also recommend using the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://wagmi.sh/">Wagmi</a> library for convenience where you can specify a default <code>dataSuffix</code> to auto-apply to your transaction requests.</p><h3 id="h-frontend-integration-with-embedded-wallets" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Frontend Integration with Embedded Wallets</h3><p>These apps enable users to receive and use wallets in an invisible manner while preserving degrees of non-custodial ownership. Users login with traditional offchain methods (e.g. phone number, email) and typically treat their account as bounded to the app.</p><p>Apps submit transactions with Embedded Wallets through their provider’s SDKs and/or APIs. Since the app has control over transaction signing, you can manually append data suffixes to transaction data and directly sign. Most embedded wallet SDKs now support <code>dataSuffix</code> parameters directly in their interfaces.</p><p>For example, you can reference these guides from <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.cdp.coinbase.com/sdks/cdp-sdks-v2/frontend/@coinbase/cdp-hooks/Functions/useSendUserOperation">Coinbase</a> and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.privy.io/recipes/evm/base-builder-codes#integrating-base-builder-codes">Privy</a>.</p><h3 id="h-backend-integration-with-server-wallets" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Backend Integration with Server Wallets</h3><p>These apps transact directly from their own servers, often with keys managed by third party providers.</p><p>Apps submit transactions with Server Wallets through their provider’s SDKs and/or APIs. This works nearly identically to embedded wallets: append your ERC-8021 formatted suffix to transaction data before signing and sending.</p><h2 id="h-start-attributing-today" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Start Attributing Today</h2><p>The foundation for a globally attributable and rewarded onchain economy is here. ERC-8021 and Base Builder Codes are the missing layer to identify the apps that generate real value.</p><p>Close the attribution gap for your app, plug into the Base flywheel, and show off your contributions to the onchain economy.</p><p>Claim your Base Builder Code and start attributing today at <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://base.dev">base.dev</a>. We look forward to seeing what you build!</p><p><strong>More resources:</strong></p><ul><li><p>ERC-8021 Standard: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eip.tools/eip/8021">eip.tools/eip/8021</a></p></li><li><p>Base Builder Codes: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/builder-codes">github.com/base/builder-codes</a></p></li><li><p>Integration Guide: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.base.org/base-chain/quickstart/builder-codes">docs.base.org/base-chain/quickstart/builder-codes</a></p></li></ul><br>]]></content:encoded>
            <author>base-engineering-blog@newsletter.paragraph.com (Conner Swenberg)</author>
            <category>measurement</category>
            <category>attribution</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/15bfda468dd57a22b17da2bc46518891ce46f965e6aaf493916c9af8d15858cd.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Anti-Sybil Infrastructure for the Base Ecosystem]]></title>
            <link>https://blog.base.dev/base-verify</link>
            <guid>VR0aDo0xpLRxZvNStJd8</guid>
            <pubDate>Mon, 09 Feb 2026 20:25:23 GMT</pubDate>
            <description><![CDATA[In this post, the Base teams dives into Base Verify: an anti-sybil service to help apps on Base confirm that users are real and unique people.]]></description>
            <content:encoded><![CDATA[<h2 id="h-the-problem-incentives-dont-scale-without-identity" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Problem: Incentives Don’t Scale Without Identity</h2><p>Rewards are one of the most powerful growth tools in crypto — and the easiest to exploit.</p><p>Across the ecosystem, teams run into the same issues:</p><ul><li><p>Bots and multi-wallet users frequently drain reward pools</p></li><li><p>Gated access leaks almost instantly</p></li></ul><p>The problem isn’t incentives themselves. It’s identity.</p><p><strong>Wallets don’t prove uniqueness.</strong></p><p>Anyone can create unlimited wallets and interact repeatedly. Without stronger identity signals, it’s nearly impossible to reward real users, prevent abuse, or build meaningful reputation.</p><hr><h2 id="h-introducing-base-verify" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Introducing Base Verify</h2><p>Base Verify is anti-Sybil verification infrastructure built into the Base App and now available to the broader ecosystem.</p><p>It allows users to prove ownership of real accounts — such as X, Instagram, TikTok, or Coinbase — without revealing credentials or personal data to applications.</p><p>Instead of tying access and rewards to wallets alone, Base Verify anchors them to <strong>real-world identity signals</strong> and returns a <strong>deterministic verification token</strong>:</p><ol><li><p><strong>The User proves they own a real account.</strong> They log in to a social platform—X, Instagram, TikTok, or Coinbase—through a familiar "Sign in with..." flow, the same kind used by countless apps.</p></li><li><p><strong>Base Verify creates a unique identifier.</strong> This identifier is tied to the user's social account, not their wallet. Critically, the same social account always produces the same identifier.</p></li><li><p><strong>Your app stores that identifier.</strong> The next time anyone tries to claim—even from a different wallet—you check if you've seen their identifier before.</p></li></ol><p>This creates a simple and powerful guarantee:</p><p><strong>One real account equals one claim — regardless of how many wallets a user controls.</strong></p><p>It also unlocks something wallets can’t: the ability to recognize high-value users even when their on-chain activity is minimal. A brand-new wallet might still belong to a Coinbase One subscriber, an X Blue user, or a creator with a large audience. Beyond proving uniqueness, Base Verify can also check whether users meet specific requirements—like having 10,000 followers, holding a Coinbase One subscription, or being a verified creator. Users prove they qualify without revealing the underlying data.</p><hr><h2 id="h-built-for-base-mini-apps" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Built for Base Mini Apps</h2><p>Base Verify already powers identity, reputation, and feed quality inside the Base App. We built it to operate at platform scale first, then opened it to all builders.</p><p>For the ecosystem, this means you get access to the same infrastructure we rely on. And because verifications are shared across the Base App, users only need to verify once. That single verification then works across every app that uses Base Verify.</p><p>Users verify once, and that verification can unlock value across multiple Mini Apps inside the Base App.</p><p>Base Verify is already live and operating at scale:</p><ul><li><p><strong>200,000+ verifications</strong> completed and growing</p></li></ul><p>It’s already used in production by Base Mini Apps today. Early partners include:</p><ul><li><p><strong>Cody</strong> Uses Base Verify to prevent reward farming and enforce one claim per real account.</p></li><li><p><strong>Scratch</strong> Distributes tokens to high-value users instead of anonymous wallets.<br></p></li><li><p><strong>Bracket</strong> Gates $BRACKY airdrops to Coinbase One members, powering sports betting incentives inside a Mini App.</p></li></ul><br><hr><h2 id="h-what-base-mini-apps-can-build-with-base-verify" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">What Base Mini Apps Can Build With Base Verify</h2><p><strong>Rewards that can't be farmed:</strong> Ensure incentives go to real people, not wallet farms.</p><p><strong>Tiered access using social proof:</strong> Unlock Mini App features based on signals like follower count or verification status.</p><p><strong>Subscriber-only benefits:</strong> Offer perks to Coinbase One members without building subscription infrastructure.</p><p><strong>Creator programs:</strong> Identify real creators using engagement thresholds instead of empty accounts.</p><hr><h2 id="h-start-building-with-base-verify" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Start Building with Base Verify</h2><p>Base Verify is available today for builders. Please fill out this <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://forms.gle/6L4hWAHkojYcefz27"><u>form</u></a> to request access to Base Verify. Check out the documentation <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.baseverifydemo.com/docs"><u>here</u></a>.&nbsp;</p><p>If you’re building incentives, rewards, gated access, or reputation systems inside Mini Apps, we’d love to collaborate.</p>]]></content:encoded>
            <author>base-engineering-blog@newsletter.paragraph.com (Rahul Patni)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/707f0f4a4c9286fa6c2b70fe86bc76c94cf87bc15f2dc17b703aebcc68c3722c.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Bridging Assets to Base: Playbook]]></title>
            <link>https://blog.base.dev/bridging-assets-to-base</link>
            <guid>2vGK4pzD8ElLNitI3nsB</guid>
            <pubDate>Tue, 27 Jan 2026 19:16:38 GMT</pubDate>
            <description><![CDATA[A playbook of practical steps for Solana and BSC projects to make their assets tradeable on Base.]]></description>
            <content:encoded><![CDATA[<h2 id="h-tldr" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">TLDR</h2><p>If your token launched on Solana, BSC, or another chain and you want it to be tradable on consumer interfaces that aggregate onchain liquidity across EVM networks, the process is straightforward: establish a verified token on your destination network, bridge supply using reputable infrastructure, seed liquidity, and publish safe, official links. This explainer focuses on Solana and BSC, with guidance that applies to other chains as well. It’s educational and venue‑neutral, and does not imply any centralized listing outcomes, endorsement of any asset, or use by any particular person or jurisdiction.</p><h2 id="h-before-you-move-make-the-token-launchready" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Before you move: make the token launch‑ready</strong></h2><p>Confirm audits and explorer verifications, set up multi‑sig and timelocks for admin functions, and prepare a concise public page with official contract addresses and links. Share enough detail to keep users safe, but avoid pre‑announcing exact deposit times or granular pool parameters that bots can exploit. Announce from official accounts, use signed messages, and maintain a single “official links” hub to reduce phishing risk.</p><h2 id="h-solana-to-base-using-the-base-solana-bridge" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Solana to Base: using the </strong><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.base.org/base-chain/quickstart/base-solana-bridge"><strong><u>Base–Solana Bridge</u></strong></a></h2><p>Solana projects can bridge to Base via the official Base–Solana Bridge, now live on mainnet and secured by <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.chain.link/ccip"><u>Chainlink CCIP</u></a> alongside Coinbase subject to applicable terms and operational assumptions. Use verified domains, start with small test transfers, and confirm transactions on explorers before publishing instructions. Share the destination token address and a short verification guide so users can independently confirm they’re interacting with the correct asset. The bridge is open‑source and available for builders to integrate, with documentation to support SOL and SPL assets in Base apps.</p><h2 id="h-bsc-to-base-secure-routes-and-configuration" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>BSC to Base: secure routes and configuration</strong></h2><p>For BSC, several established bridges support movement into Base, including <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://wormhole.com/products/native-token-transfers"><u>Wormhole</u></a>, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.layerzero.network/v2/developers/evm/overview"><u>LayerZero</u></a> (OFT for unified supply or Stargate for liquidity bridging), and options like <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.axelar.dev/"><u>Axelar</u></a> or <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.debridge.com/"><u>deBridge</u></a>. Begin with small transfers, configure rate limits and endpoint monitoring, and publish destination addresses and explorer links so wallets and aggregators recognize your asset.</p><h2 id="h-make-the-token-tradable-seed-liquidity-across-platforms" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Make the token tradable: seed liquidity across platforms</strong></h2><p>Once your token exists on the destination network, create pools on one or more venues so aggregators can route trades. On Base, popular choices include <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://aerodrome.finance/docs"><u>Aerodrome</u></a> and launch tooling like <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.flaunch.gg/"><u>Flaunch</u></a>; cross‑chain options include <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.uniswap.org/"><u>Uniswap</u></a>, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.sushi.com/"><u>Sushi</u></a>, and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.mav.xyz/"><u>Maverick</u></a>. Pick a common pair (e.g. token/ETH or token/USDC), set a fee tier that compensates LPs for early volatility, and consider layered ranges if you use concentrated liquidity. To reduce sniping, share pool details only after onchain confirmations rather than pre‑publishing exact timestamps or ranges.</p><h2 id="h-broaden-access-beyond-one-network" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Broaden access beyond one network</strong></h2><p>Many teams improve accessibility by maintaining liquidity across multiple platforms and, where appropriate, mirrored pools on other chains. If you use project‑owned liquidity, disclose it and size it prudently so it doesn’t dominate active liquidity or distort price discovery.</p><h2 id="h-operational-safety-you-should-bake-in" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Operational safety you should bake in</strong></h2><p>Use multi‑sig for admin changes, timelocks for sensitive operations, and segregated operational wallets. Monitor bridge events and pool health (TVL, fees, LP concentration, slippage). Coordinate with indexers and analytics sites to display verified logos and addresses, and keep your official links page current to reduce spoofing.</p><h2 id="h-closing" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Closing</strong></h2><p>A multichain approach to onchain liquidity is repeatable when you combine reputable bridging, verified contracts, and measured liquidity seeding. Publish enough detail to keep users safe, avoid granular pre‑announcements that enable bots, and consider multi‑platform liquidity to broaden access. This explainer covers mechanics—not outcomes.</p><h2 id="h-disclaimers" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Disclaimers</strong></h2><ul><li><p>Use of bridges, pools and onchain applications may be restricted or prohibited in certain jurisdictions or for certain persons (including under sanctions or AML regimes). You are responsible for compliance with applicable laws, including sanctions, AML and consumer protections laws.</p></li><li><p>Nothing herein constitutes facilitation, brokerage, solicitation, distribution or intermediation of any token, protocol or transaction and does not create any agency, partnership or advisory relationship.</p></li><li><p>Educational, venue‑neutral content. This does not advise on token sales, fundraising, pricing, investor targeting, regulatory classification, or legal risks. It is not legal, tax, or investment advice; consult your own counsel.</p></li><li><p>Availability via onchain interfaces is independent of centralized listing decisions. No guarantees, signals, or implied likelihood of listing. Choices of bridge, venue, or chain do not affect listing outcomes.</p></li><li><p>References to bridges and tooling (including the Base–Solana Bridge, Chainlink CCIP, Wormhole, LayerZero, Axelar, deBridge, Aerodrome, Flaunch, Sushi, Maverick, Uniswap) are examples; availability and security characteristics may vary. Always verify official documentation, endpoints, and contracts before use.</p></li></ul><br>]]></content:encoded>
            <author>base-engineering-blog@newsletter.paragraph.com (Nick Shaheen)</author>
            <category>bridging</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/de0403a5a4d84a1529057fc3d1442f4d429685295486e20f76eb4d5ebdb548f6.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Building The New Base App: Prefetching at Scale]]></title>
            <link>https://blog.base.dev/base-app-prefetching-at-scale</link>
            <guid>hY0Gr9ht2h7PmXfCa7U8</guid>
            <pubDate>Wed, 21 Jan 2026 19:00:57 GMT</pubDate>
            <description><![CDATA[In this post, the Base App team dives into reducing navigation blocking time by 80-100% and eliminating loading shimmers entirely.]]></description>
            <content:encoded><![CDATA[<h2 id="h-background-the-challenge" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Background: The challenge</h2><p>A year ago, we made a bold decision: evolve Coinbase Wallet into the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://join.base.app/"><u>Base App</u></a>, an onchain everything app that brings social, trading, payments, app discovery, and earning into one place. Our goal was to build a consumer-grade experience that rivals the best social apps, while gracefully handling onchain challenges like transaction confirmations and real-time blockchain data syncing. But we also knew it was critical to balance security and quality with speed and scale. To do this, we carefully considered how to handle loading states.</p><p>We started with loading skeletons (or shimmer UIs). They serve a single purpose: to keep users engaged while data is being fetched. Instead of staring at a blank screen or a spinning loader, users see a visual placeholder that mimics the final layout of the content.</p><p>But loading skeletons are ultimately an elegant <strong>band-aid</strong>, and we set out to do better. They help mask slow APIs or heavy computations by giving users something to look at while they wait. While they improve the overall user experience, they don't address the root cause of slow performance.</p><p>As the content is being fetched, the skeleton itself must be rendered first, followed by the actual content once it’s loaded. This double render cycle can affect performance, particularly on less powerful devices. The result? An additional delay that might ironically make the app feel slower, especially in low network conditions or on resource-constrained devices.</p><p>One could also argue the repeated, jumpy flash of gray shimmers isn’t as elegant as we thought. See our current Profile loading timeline:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e9f4db2a4217bf7154be804c07a78130fa9b6c72b470c755e6579aafc334491a.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAOCAIAAADBvonlAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEF0lEQVR4nI2SwU/bdhTHfd6fMcSBSrv1MG07oR5Wpna7tJE2dZoGYwfGYJ0Yp9aEMhjQoWW1XFCmYObWtTBWMwyZUUaWKUKUVkUJoSEimxMCS5Zgkvzwj1+I4+VNsRnqYYe9w09PX733Pnrv+6MAgBCCEAIAbMd/KqfV6pGuA4BpmrqdAICu66ZpOollWQBQKpWc+vOEIoSwLNve3k5RVFtbm9vtzuVyXq/3ZQUhFAqFvrh5k7Lj26mpaDSaSCSmpqYcZXBwMBAIYIy93h9aWlooinK5XD/OzRFCKE1LT0+zCCFZlnVd53ne7/d7vV6MsSzLhUKB5/nNzc33rl5lvvdsxbfD4d86O7tmOe6bsbEPrl/fTf0eDP4yOjr65cBAKBR6/eLFZDK5uhqSpIXLlzsKhQKl60dvvfHmVix2mM8CNGRZVlXV55t1rgEAgiDsJJOvXbjwbkcHQC27l361peXBw4f0rVuvUNRhPntaPXnnypXOzq5wOExR1Mb6GkB9ZGSstbUVY0wdHBxMTk4gVElrGiFEFEVVVTmOM00rl8sDgCRJkUjk/vT0ohKYFx/lcn/uplKPBEGSpMeLywvSfDy+peu6IAjBYJDnHyjK0spKAABmZmY0TWsCOI5zjHLGOQDHKLCVtbU1lmU1Tcvl8pZlJZNJWZYZhvnk4xsvEjt/aGlN03ieX11dvfHRh+tPNnaSu5ZlMQyTyWSagJmZ+wCA0LE9biEQCAiCYCvIUSKRiCiKlh0AkEqlJEkSBEFRFIxPMMalUumRKC4vL7muXYtGt7ZjzzHGLMueARiGKaF8PPE8kUp47n2nqqoDIIQAgN/vD4fDoiiaZv0cIIqiLMuapgGAZVkIIUEQVFXt/7yP5333PHcDgaWJyckzAMfNPnkanv9pdiW4ODI8dA5wTPb/C3CmOwBBEGRZTqVSjlIqlXieD4VCiqLY72I8Hh0fn2gC9vayczwX+nVlds7rX17o7ev9PwDxpQ2cY4qiGAwGJUmKxWJP1yMGQmceZDIZhmFyuYNnzzYyWY2m6SVF8fl8AHBkm8zzfCQS4TgOIYQxtixrK7bF87woivH4tmVZhBDnpyiK8valS7Ls//mxYCB0tgEhxOPxuN3unp6eIZoeHx8vFAosy9I0/Wl39xBND98ZJoTIskzTt3t7Pxsc/MrtdicSL6LRqNvtHhgY6O/vo+nbiqIQQkZHv+7q6na53h++M+zxeEzTpACgAbBfRPtFlC1WqrX6Manlj4x0vpzOlw8rJ1aj4SiZv8rZYiVdqByTWrVWzx8da/nSfhGl8+XyyWnd+rtsVPeLlWyxnLPbG/b1mgD7w1QryDAwIaR6WqsTUjUIKSEDGU3FajRM03KUSlNq1hiEIAMb+MQuw+eNyHAasWPhPwDrBUEy9vP2AAAAAElFTkSuQmCC" nextheight="686" nextwidth="1600" class="image-node embed"><figcaption htmlattributes="[object Object]" class=""><em>Profile Loading States</em></figcaption></figure><p>This pattern addresses a backend bottleneck : Loading data from multiple sources, all at once can be slow. It introduces a waterfall of layout loading state while various parts of the screen are being fetched.</p><p>However, this came at the tradeoff of making the user's experience substantially worse.</p><p>We wanted to do better. So we built a behavioral prefetching system in the new Base App to anticipate user behavior and deliver content instantly.</p><h2 id="h-solution-behavioral-data-driven-prefetching" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Solution: Behavioral, data-driven prefetching</h2><p>We have <em>data</em> on the navigation habits between different links and screens. E.g: Users on the Feed are more inclined to navigate to a <strong>Cast</strong> than a <strong>Profile</strong>.</p><p>We also know when links are <strong>in the viewport</strong>. We can use this to design a simple prefetching algorithm:</p><pre data-type="codeBlock" text="score = conversionRate * visibility * manual priority"><code>score <span class="hljs-operator">=</span> conversionRate <span class="hljs-operator">*</span> visibility <span class="hljs-operator">*</span> manual priority</code></pre><p>And start prefetching screens data accordingly:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/ea627e79fcad69eea2e67283f69b350ce8eece2d0698762aa1f4b93462006999.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAgCAIAAACO148VAAAACXBIWXMAAAsTAAALEwEAmpwYAAAGx0lEQVR4nLWWX0wbRx7H/RBd+3q6a093OuWlD0kkGkWqTzrlDXENhVBHIlLkQ0hX9aSIvuTBRX6q0t5dLqc6NIj0z11Clf4hNSlgYxzjZVl2Q8waDAnGAYxhMcZ4Wcx6za69Xq/XjO2p7AHzp4nu5e6n0Xh2NPvx9/eb38xvdfD/YDr0UyoWdrNSpaWAmgZqejeb2s1KICcXQb4Icqgv5HMglykvyGUK+SxQ0+U+pxTyWQhLh6ElCCHIZfhlD79MC+FpfplOMF4hPLO9SOxEAzk5qYpcLhXX5ER6czEZmeaXKG5xTAg/5RbHEgzNL1FCeCavqkeUFiuGFEMINXlLFaPVx8qC44MX2j4E6tBoanLy88/v3P6s41ZHx+1P//7Zvz6+1dHRcevWna6u8fHx6gscx33d3X2nq8ti+bSz83aldVoslq6uLqvVKsvygVKCIM6cOdPW9sF77/1Fd8hqa2vb29tPnDhx9+5dCKGiKH+qqzt//vzNmzcPL9PpdBaL5eTJk3V1dejvy9Br16719PSg5wna29/7ne3H76nH4wAACKHP56uvr4cQPnz4sK2tDWmJRCIDAwMOh8Nut/M8jybr698JhZb3lJrN5u/v/xsAleO28gBUckCEEKqqqmnawsKCwWBA0NbWVu/ktCiKx0KpaRqEsLW1dWFhYQ9qMplcLld5izQNFEtqOllOpmIJxTEajTY1NUEIx0gSOWs2mRBrPbqxFokOOpxo/hevvIJUl6H/uPFPnU73t48+mvX7fT7fBO19TlpVkUM7XYW6XK5f//JXOp2u+969EoR8bCm5FVHU/OrqanPz5YtNTW/p9ZFIZA/68Sef/P611777ujshCLFYjGU34+x6StyRZblYLEYiEQS1Wq3vv//XELNGkpTLTeAD9xJhf+Xg7GVkS0vLC9wXRTGrSFklk0pnkjuiIAiapoXDYQRFMfX5fDTtJXBi0vdsgvYF5uYWg0EPTS+FQs3NzcFgcA/6ock0ODgIIYzH4+lUSipbKiWJspwuFkuHoZcvX56cnKJpGsMwm82GYVgwuKRpmiRJxWLRaDQeKP1wXynablVVc5qmaeUOZU8V2tJidI/hgeeLLLs5v7C4Ht2IRjdkWRYl6aXu8zwvVkwSk4qiiKIIAKhC+/r6L1688E5Dbb/dyXFb5WVSShCSCUFICMJLoal0SlVVRcnmVAUAgMJfhfZarW1tH6yGI5vs5uxsILmzsxFjxXSmeoiPQx0OB3IfXS6gUKjeMmv7UJvdfuHtt7s6Ong+sR5Z34rz3PpKMhbKqho6Dn8+HFOz2dzZeTudSu8kyx4pSjYjZwAAqporgEJ1o0aJMZ1O96pON4KPWq29D/sGe7/9ihr6FsPJMZLM7+4e2SjTvtLN+CrLh2PcygbLMGuhJWZxORwkH5PomPb09LS3t0MISZIaw53B516ceGwbdGIY5vF4RFE87j6GYRDCuSUqsDQ+Exj1+QlqinBRDtKLjeKPDIZLCGo2myGEQ0NO+8ADenyYICi32z3k6CNJKh6PHz/77mF3+Q4cc3i9+LSfnAmMj07gM7NTy4FZmhqrKkXQybJNzS8s+v3+2dmnmxE/zyc0Tfu50hEI4Y991t4HPxDU0OQz0u6y+SafPJ3y2u39x6Aej4cgRp1OJ0USFEWOe7yPHrlEUTyuFCtDS5RneGBggBofoX3E6ATuHhkadNl6+62X9t1HMR3B8VFiFCeoJ7iNmZ/hheQmy74gpbAKlHji7On5wemykR4X/gRzU86Bob6v7n6Jdr+qlOeFrKpKUiotKwlhB2W0quauXLlycPZNJtNwJaYzszSO4SPY8MwsPe2fmvZPESRxp+uLhoYGCKHb7f7d66/Xnf+j3x8gcJx8POEZfsBMO8MRNhhcAgAYjcYjUJRS6F6opj0oFCCEDMM0NjZCCJ1O56k33rhiMIRWmMDc3PLKytpUvyZv5QtFtVKfj+Tp9evXr169KssywzA4jvdVzOVyMQwjSdKNGzeMRiOquO8aDKBYYkLPv/nm/rvGa198+Z/u7vuhUEgQBJqma2rOooJahiqKYjQaa2trDYZLev0fampq3jx79i29vrGx8UJ9/YX6+lgshsqRxWLRl+cbzp0795vfnjx9+vSpU6fr6uoMBoNer7darQfVtPoRUB0cs9JL5v/LtxSoVGMpk/WFYisbiflw3M9wq6yg7QJ2W+SSadRi2+J8mGNYYToUCzBcdFt8FmJD0QQiIMgBFJm2C9a45GYitcYlI1s7W0IqD4CUySlqXtXKLacBNpHa3pFXWWGNS7KJ1Hx4a337SMU+Dv1f2U94EVQs6rba4AAAAABJRU5ErkJggg==" nextheight="1600" nextwidth="1424" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>A simple idea in principle, with many caveats.</p><h3 id="h-overfetching" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Overfetching</strong></h3><p>There can be hundreds of links on any given screen — so how can we reduce scope and only prefetch visible items? Unfortunately react-native doesn’t have yet an <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API"><u>Intersection Observer API</u></a> so we came up with a few alternatives below.&nbsp;</p><div data-type="callout" type="info"><link rel="preload" as="image" href="https://paragraph.com/editor/callout/information-icon.png"><div class="callout-base callout-info" data-node-view-wrapper="" style="white-space:normal"><img src="https://paragraph.com/editor/callout/information-icon.png" class="callout-button"><div class="callout-content"><div><p><strong>Note:</strong> These snippets are meant to be educational / inspirational, copy-pasting as-is won’t work.</p></div></div></div></div><h3 id="h-measure-and-onlayout" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Measure &amp; onLayout</h3><p>With react’s&nbsp;<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://reactnative.dev/architecture/landing-page"><u>New architecture</u></a>, we can leverage the useLayoutEffect &amp; <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://reactnative.dev/docs/the-new-architecture/layout-measurements"><u>measure method</u></a></p><pre data-type="codeBlock" text="function PrefetchOnVisible({ children, prefetchQuery }) {
  const ref = useRef&lt;View&gt;(null);
  const prefetchManager = PrefetchManager.getInstance();

  // Synchronous, first-frame measure
  useLayoutEffect(() =&gt; {
    ref.current.measure((_x, _y, _width, _height, _pageX, pageY) =&gt; {

      // Within Viewport?
      const isComponentVisible = pageY &lt;= viewPortHeight &amp;&amp; pageY + 100 &gt;= 0;
      if (!isComponentVisible) return;

      prefetchManager.process(prefetchQuery);
    });
  }, [prefetchQuery]);

  return &lt;View ref={ref}&gt;{children}&lt;/View&gt;;
}"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">PrefetchOnVisible</span>(<span class="hljs-params">{ children, prefetchQuery }</span>) </span>{
  const ref <span class="hljs-operator">=</span> useRef<span class="hljs-operator">&lt;</span>View<span class="hljs-operator">&gt;</span>(null);
  const prefetchManager <span class="hljs-operator">=</span> PrefetchManager.getInstance();

  <span class="hljs-comment">// Synchronous, first-frame measure</span>
  useLayoutEffect(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    ref.current.measure((_x, _y, _width, _height, _pageX, pageY) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {

      <span class="hljs-comment">// Within Viewport?</span>
      const isComponentVisible <span class="hljs-operator">=</span> pageY <span class="hljs-operator">&lt;</span><span class="hljs-operator">=</span> viewPortHeight <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> pageY <span class="hljs-operator">+</span> <span class="hljs-number">100</span> <span class="hljs-operator">&gt;</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>;
      <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>isComponentVisible) <span class="hljs-keyword">return</span>;

      prefetchManager.process(prefetchQuery);
    });
  }, [prefetchQuery]);

  <span class="hljs-keyword">return</span> <span class="hljs-operator">&lt;</span>View ref<span class="hljs-operator">=</span>{ref}<span class="hljs-operator">&gt;</span>{children}<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>View<span class="hljs-operator">&gt;</span>;
}</code></pre><p>For legacy Architecture, we can fallback to <strong>onLayout</strong> callback:</p><pre data-type="codeBlock" text="function PrefetchOnVisible({ children, prefetchQuery }) {
  const ref = useRef&lt;View&gt;(null);
  const prefetchManager = PrefetchManager.getInstance();

  // Asynchronous, next frame measure
  const onLayout = useCallback(() =&gt; {
    ref.current.measure((_x, _y, _width, _height, _pageX, pageY) =&gt; {

      // Within Viewport?
      const isComponentVisible = pageY &lt;= viewPortHeight &amp;&amp; pageY + 100 &gt;= 0;
      if (!isComponentVisible) return;

      prefetchManager.process(prefetchQuery);
    });
  }, [prefetchQuery]);

  return (
    &lt;View ref={ref} onLayout={onLayout}&gt;
      {children}
    &lt;/View&gt;
  );
}"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">PrefetchOnVisible</span>(<span class="hljs-params">{ children, prefetchQuery }</span>) </span>{
  const ref <span class="hljs-operator">=</span> useRef<span class="hljs-operator">&lt;</span>View<span class="hljs-operator">&gt;</span>(null);
  const prefetchManager <span class="hljs-operator">=</span> PrefetchManager.getInstance();

  <span class="hljs-comment">// Asynchronous, next frame measure</span>
  const onLayout <span class="hljs-operator">=</span> useCallback(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    ref.current.measure((_x, _y, _width, _height, _pageX, pageY) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {

      <span class="hljs-comment">// Within Viewport?</span>
      const isComponentVisible <span class="hljs-operator">=</span> pageY <span class="hljs-operator">&lt;</span><span class="hljs-operator">=</span> viewPortHeight <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> pageY <span class="hljs-operator">+</span> <span class="hljs-number">100</span> <span class="hljs-operator">&gt;</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>;
      <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>isComponentVisible) <span class="hljs-keyword">return</span>;

      prefetchManager.process(prefetchQuery);
    });
  }, [prefetchQuery]);

  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&lt;</span>View ref<span class="hljs-operator">=</span>{ref} onLayout<span class="hljs-operator">=</span>{onLayout}<span class="hljs-operator">&gt;</span>
      {children}
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>View<span class="hljs-operator">&gt;</span>
  );
}</code></pre><p>Then wrap our links accordingly:</p><pre data-type="codeBlock" text="function ProfileLink({ children, profileId }) {
  const navigateToProfile = useCallback(() =&gt; {
    navigate('Profile', { profileId });
  }, [profileId]);

  const profileQuery = useProfileScreenContentQuery(profileId);

  return (
    &lt;PrefetchOnVisible prefetchQuery={profileQuery}&gt;
      &lt;Pressable onPress={navigateToProfile}&gt;
        {children}
      &lt;/Pressable&gt;
    &lt;/PrefetchOnVisible&gt;
  );"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ProfileLink</span>(<span class="hljs-params">{ children, profileId }</span>) </span>{
  const navigateToProfile <span class="hljs-operator">=</span> useCallback(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    navigate(<span class="hljs-string">'Profile'</span>, { profileId });
  }, [profileId]);

  const profileQuery <span class="hljs-operator">=</span> useProfileScreenContentQuery(profileId);

  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&lt;</span>PrefetchOnVisible prefetchQuery<span class="hljs-operator">=</span>{profileQuery}<span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span>Pressable onPress<span class="hljs-operator">=</span>{navigateToProfile}<span class="hljs-operator">&gt;</span>
        {children}
      <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>Pressable<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>PrefetchOnVisible<span class="hljs-operator">&gt;</span>
  );</code></pre><p>If <strong>ProfileLink</strong> is within the viewport when the screen renders, <strong>ProfileQuery</strong> will be fetched &amp; cached. Navigation to the profile will become instant.</p><p>However, this only works for static, non-scrollable screens: useLayoutEffect and useLayout only fire when the layout changes.</p><h3 id="h-virtualized-list-renderitem-and-onviewableitemschanged" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Virtualized list: renderItem &amp; onViewableItemsChanged</h3><p>Most if not all virtualized lists (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://reactnative.dev/docs/flatlist#onviewableitemschanged"><u>FlatList</u></a>, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://shopify.github.io/flash-list/docs/usage#onviewableitemschanged"><u>FlashList</u></a> &amp; <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.legendapp.com/open-source/list/v2/props/#onviewableitemschanged"><u>LegendList</u></a>) have two methods we’re interested in:</p><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://reactnative.dev/docs/flatlist#required-renderitem"><u>renderItem</u></a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://reactnative.dev/docs/flatlist#onviewableitemschanged"><u>onViewableItemsChanged</u></a></p></li></ul><p>We can wrap these methods to keep track of when an item is visible on the screen:</p><p>Let’s start by wrapping renderItem function with a provider to share the item’s index:</p><pre data-type="codeBlock" text="function usWrappedRenderItem({ renderItem, listId }) {
  
  // Wrap list items with a provider surfacing the index of the item
  return useCallback((args) =&gt; (
    &lt;ListItemVisibilityProvider listItemVisibilityIndex={args.index} listId={listId}&gt;
      {renderItem(args)}
    &lt;/ListItemVisibilityProvider&gt;
  ), [renderItem]);
}"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">usWrappedRenderItem</span>(<span class="hljs-params">{ renderItem, listId }</span>) </span>{
  
  <span class="hljs-comment">// Wrap list items with a provider surfacing the index of the item</span>
  <span class="hljs-keyword">return</span> useCallback((args) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> (
    <span class="hljs-operator">&lt;</span>ListItemVisibilityProvider listItemVisibilityIndex<span class="hljs-operator">=</span>{args.index} listId<span class="hljs-operator">=</span>{listId}<span class="hljs-operator">&gt;</span>
      {renderItem(args)}
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>ListItemVisibilityProvider<span class="hljs-operator">&gt;</span>
  ), [renderItem]);
}</code></pre><p>All children within this function can now access their index within the list &amp; the listId:</p><pre data-type="codeBlock" text="const { index, listId } = useListItemVisibility()"><code>const { index, listId } = <span class="hljs-built_in">useListItemVisibility</span>()</code></pre><p>Next let’s wrap the onViewableItemsChanged callback:</p><pre data-type="codeBlock" text=" function useVisibilityTracker({ onViewableItemsChanged, listId }) {

  // 1. Keep track of mounted list
  useEffect(
    function trackListVisibility() {
      visibilityTracker.registerList(listId);

      return visibilityTracker.removeList(listId);
    },
    [listId],
  );

  // 2 Update visible items when array of visible items changes
  const updateVisibleItems = useCallback((visibleIndexes: Set&lt;number&gt;) =&gt; {
    visibilityTracker.setVisibleItems(listId, visibleIndexes);
  }, [listId]);

  // 3. Debounce to avoid rapid-fire updates while scrolling
  const debouncedUpdateVisibleItems = useDebouncedCallback(updateVisibleItems, 150);

  // 4. Return the wrapped onViewableItemsChanged callback
  return useCallback((event: { viewableItems: ViewToken[]; changed: ViewToken[] }) =&gt; {

    // Call the original callback first
    onViewableItemsChanged?.(event);

    // Get visible indexes
    const { viewableItems } = event;
    const visibleIndexes = new Set(
      viewableItems.filter(({ isViewable }) =&gt; !!isViewable).map(({ index }) =&gt; index),
    );

    debouncedUpdateVisibleItems(visibleIndexes);
  }, [debouncedUpdateVisibleItems, onViewableItemsChanged]);
}"><code> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useVisibilityTracker</span>(<span class="hljs-params">{ onViewableItemsChanged, listId }</span>) </span>{

  <span class="hljs-comment">// 1. Keep track of mounted list</span>
  useEffect(
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">trackListVisibility</span>(<span class="hljs-params"></span>) </span>{
      visibilityTracker.registerList(listId);

      <span class="hljs-keyword">return</span> visibilityTracker.removeList(listId);
    },
    [listId],
  );

  <span class="hljs-comment">// 2 Update visible items when array of visible items changes</span>
  const updateVisibleItems <span class="hljs-operator">=</span> useCallback((visibleIndexes: Set<span class="hljs-operator">&lt;</span>number<span class="hljs-operator">&gt;</span>) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    visibilityTracker.setVisibleItems(listId, visibleIndexes);
  }, [listId]);

  <span class="hljs-comment">// 3. Debounce to avoid rapid-fire updates while scrolling</span>
  const debouncedUpdateVisibleItems <span class="hljs-operator">=</span> useDebouncedCallback(updateVisibleItems, <span class="hljs-number">150</span>);

  <span class="hljs-comment">// 4. Return the wrapped onViewableItemsChanged callback</span>
  <span class="hljs-keyword">return</span> useCallback((<span class="hljs-function"><span class="hljs-keyword">event</span>: </span>{ viewableItems: ViewToken[]; changed: ViewToken[] }) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {

    <span class="hljs-comment">// Call the original callback first</span>
    onViewableItemsChanged?.(<span class="hljs-function"><span class="hljs-keyword">event</span>)</span>;

    <span class="hljs-comment">// Get visible indexes</span>
    const { viewableItems } <span class="hljs-operator">=</span> <span class="hljs-function"><span class="hljs-keyword">event</span></span>;
    const visibleIndexes <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> Set(
      viewableItems.filter(({ isViewable }) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> <span class="hljs-operator">!</span><span class="hljs-operator">!</span>isViewable).map(({ index }) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> index),
    );

    debouncedUpdateVisibleItems(visibleIndexes);
  }, [debouncedUpdateVisibleItems, onViewableItemsChanged]);
}</code></pre><p>And finally, use this in our lists:</p><pre data-type="codeBlock" text="const wrappedRenderItem = useWrappedRenderItem({
    renderItem,
    listId: 'MyList',
});

 const wrappedOnViewableItemsChanged = useVisibilityTracker({
    onViewableItemsChanged
    listId: 'MyList',
});

return (
  &lt;FlashList 
    renderItem={wrappedRenderItem}
    onViewableItemsChanged={wrappedOnViewableItemsChanged}
    ..."><code>const <span class="hljs-attr">wrappedRenderItem</span> = useWrappedRenderItem({
    renderItem,
    listId: 'MyList',
})<span class="hljs-comment">;</span>

 const <span class="hljs-attr">wrappedOnViewableItemsChanged</span> = useVisibilityTracker({
    onViewableItemsChanged
    listId: 'MyList',
})<span class="hljs-comment">;</span>

return (
  &lt;FlashList 
    <span class="hljs-attr">renderItem</span>={wrappedRenderItem}
    <span class="hljs-attr">onViewableItemsChanged</span>={wrappedOnViewableItemsChanged}
    ...</code></pre><p>With this,&nbsp; we effectively gave our list items full visibility context, so we can use this to prefetch our data like so:</p><pre data-type="codeBlock" text="function PrefetchListItemOnVisible({
  children,
  prefetchQuery,
}) {
  const visibilityTracker = ListVisibilityTracker.getInstance();
  
  // from wrappedRenderItem
  const { index, listId } = useListItemVisibility();

  // Subscribe on mount
  useEffect(() =&gt; {
    visibilityTracker.subscribe('listId', (visibleIndexes) =&gt; {

      // Item index is not in the viewport, return
      if(!visibleIndexes.has(index)) return;

      // Item is in the viewport, prefetch !
      prefetchManager.process(prefetchQuery);
    });

    return () =&gt; visibilityTracker.unsubscribe();
  }, []);

  return children;
}; "><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">PrefetchListItemOnVisible</span>(<span class="hljs-params">{
  children,
  prefetchQuery,
}</span>) </span>{
  const visibilityTracker <span class="hljs-operator">=</span> ListVisibilityTracker.getInstance();
  
  <span class="hljs-comment">// from wrappedRenderItem</span>
  const { index, listId } <span class="hljs-operator">=</span> useListItemVisibility();

  <span class="hljs-comment">// Subscribe on mount</span>
  useEffect(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    visibilityTracker.subscribe(<span class="hljs-string">'listId'</span>, (visibleIndexes) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {

      <span class="hljs-comment">// Item index is not in the viewport, return</span>
      <span class="hljs-keyword">if</span>(<span class="hljs-operator">!</span>visibleIndexes.has(index)) <span class="hljs-keyword">return</span>;

      <span class="hljs-comment">// Item is in the viewport, prefetch !</span>
      prefetchManager.process(prefetchQuery);
    });

    <span class="hljs-keyword">return</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> visibilityTracker.unsubscribe();
  }, []);

  <span class="hljs-keyword">return</span> children;
}; </code></pre><p>The hard part is done, and what’s next is we have&nbsp; scroll-aware viewport context and can prefetch data accordingly.</p><h3 id="h-backend-strain" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Backend strain:</strong></h3><p>Even when fetching only visible data, this can still mean hundreds of requests per user, multiplied across hundreds of thousands of users.</p><h3 id="h-its-queue-time" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">It’s queue time&nbsp;</h3><p>An on-device queue gives us flexibility and safety, we can add delay, max concurrent requests &amp; even a set maximum request per minutes:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/4cd6283975370b7d06d9903ce2e18e785213962fa72ff320a93c699f8630ce73.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAOCAIAAADBvonlAAAACXBIWXMAAAsTAAALEwEAmpwYAAACcElEQVR4nLWTTWgTQRTH9+hBCLQoFERka6Ey9tDaBUVZUMHG+oFsEUS72FtU6kc3tGLS8aDGDBTEw2iPc0yZWkQoo2CFdvAgFAYtVWRTi2C6lB6StFPsIYGR7IbYQNrGg7/D8mZ58/77/u+tpnwKxUKhWFD/Ac3zvNGXoxu/N7ZJSqcXMMac83oqSrleJTD3df5sT3fvzevRh4P808egG6VUNpeVUgYXcvm867rBsbqW3EqjIqOJL5977MsN+/bs2rv7zIWu8YlXSqm5+Tk29Xbs9fjiz0WlVC6fn5mZppQyH845pVQIoZRyXbfyUvgkRp5nlry/HaQmUu0d7Z0dneAQaDnYAoy289cuxh4P9w/dffAo1j90+150oLm5ORwOE0I0TQuFQrquu64bDocBAJqmIYQ0H9u2U2Nj+1uPHDjcefRU97up6fzqWqmDq329x0+eaG1rjcYHn4wksrns++kPiWfJ1Bua/rGQTi9MTpa+kRBiWZZhGI7jEEIYY0KIIEAIRSKRZPLp7Oxs3407x06f67p05dbA/W/f01rQCOc8EolUBhD4W7FYynUhBKUUQggAYIxRSj3P25y5Ob9qyEopjHFjQyMAwHGcmkP7lck4jtPU1BQKhYKnaZqU0m0mLOV6oVgsC3DObR/GWM07mSWP+EAILctCCEEI69zakoCU0nVdCGHNzSsUi/nVNc+Hcx6kCSEqFu0gIKVECFk+GGPGWFAoQPhg/IJSGovHdV0HAJimWf9/VxYwDEPXdQghQohzjjEOYgghISQ2XI5NH9u2KaX/YJHneVLKwITtLaqsylY7U1tgR5aXV+pJqynwB+qEOYjljmNpAAAAAElFTkSuQmCC" nextheight="684" nextwidth="1600" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>We also can set these queue options based on device class: A last-gen iPhone Pro can prefetch hundreds of queries in seconds, but a low-end Android phone will start struggling almost immediately.</p><h3 id="h-killswitches" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Killswitches</h3><p>Even the most moderate queue settings and conservative visibility tracking can’t avoid overfetching entirely: We added fine-grained killswitches by defining “Triggers,” e.g. Link to a screen &amp; “Target” e.g. The screen.</p><p>If our profile backend sees an abnormal strain from prefetching, we can disable specific prefetching flow like “search results → profile”, or disable profile prefetching entirely.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e79d28ef49b28180e5a1353bca0f354b563a8ca065c5c42543225a229651da0a.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAVCAIAAACor3u9AAAACXBIWXMAAAsTAAALEwEAmpwYAAADPElEQVR4nKWUT2gbRxSHlxwDoQVDClvcgg6JyGlzU8AH0X/YvVite9g4+FQfolJTuj203TS0eJ24bSYhaTQtjSlTmxatgxOrG1RqDfEhWaUIowWbSOOGJnZHxkQalEs7AiGLLZqJFceVjKJ+CLGH2ffNe7/ZUfyu2KrXt+r1TlYqXVSfmb1+9jw8+aF55cd4R4LNh8ViiW0+LFYqlT2Wcs4xxr/fuTOf+OWbGBz76OOJya9d9zbGmHPeVrBVr7/7nvG+YR48dPTit1O+X+ecM0GzLhfvY4xd16W0wFiJsdJGobBRKDDGbNtuLm4hoJSmUgvTCEUFhmEAADDGAADbtncu9TyvZSHXdfcSOI7DGHNdNxwOh0IhTdP6+/sppRDCYDCoCcICVVW7ETDGEEKmaRqGcerUZ5ZlAQAsy4IQcs4ppd42wWCQEPLMAkopAMBxHNu2HYEtQAjtis4RvT6zgBBiWVaPQFXVQCDQ09Oj63o0Go0IwuGwpmmqqipKYzfN2HeG/19BRdAQcM7lrqEAIeQ4DsYYIcQYk1XkA4RQCnYhBeb4V30DQ8OjY30DQ4MnRosl9iQDCKEcvSmQzxBCWdffBgDAOfc8T7al67plWZRSOaKl7HIimUpnskvZlXQmm85kP/jkC28l1zhFgUBAeRpN00KhUCQS0XXdEEQiETkiQkg0GpUzBABQSjHGlNJc/t7d/OpSdln+pzPZ0xPn7uZXGxkggWmaAIDLsRhCqGXICKGWI2oX/uMR+R0DtzPYFXJLQfM2VCqVSjqTXVi85a3kEslUIplaWLzVjMjfcR667ODvf/i1G7+9/tbwsdcGT0+ce/OdkVcix8/HrhRLzFvJ3V8rSIfv+7Zt73GK2gp8308kU9+jnxPJVHwu8dPV+ficcyT0at/A0IGXj7zx9sgZcPma8+v/EkxNxz8/A768ACcvxIxPxy9998O+FwLKcy/u7w0eeCmoKM/v7z08M3vddW938yX7vl8uP6rWatVarVx+VGLlaq22tv5XLn8vT/5YpxuVavXPB2slVl68eRNjTAhxXRcLxO1N23X2RNAh8isjhMgrS0IEe7y1W1Ct1WRPzbaav863slPwL/HuiHWfhFExAAAAAElFTkSuQmCC" nextheight="1033" nextwidth="1600" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Now that frontend won’t overfetch, and we have multiple mitigations in place for our backend. Time for the elephant in the room: DevX</p><h3 id="h-devx" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><span data-name="elephant" class="emoji" data-type="emoji">🐘</span> <strong>DevX</strong></h3><p>We have 20+ classes, components, context, helpers, hooks. How can we make the process of <em>implementing</em> prefetching easy?</p><p><strong>Solution:</strong> a single API for prefetching target &amp; triggers:</p><pre data-type="codeBlock" text="function createPrefetchableComponent( { query, prefetchTarget, options }, Component) {

  // Wraps &quot;target&quot; component
  const PrefetchableComponent = memo(function PrefetchableComponent({ variables }) {

    // Fetching &amp; refresh methods
    const queryRef = useLazyLoadQuery(query, variables, options);
    const { refresh, isRefreshing } = useRefreshQuery(query, variables);

    const props = { queryRef, refresh, isRefreshing, variables };

    return &lt;Component {...props} /&gt;
  });

  // Attach the Trigger component with the query &amp; prefetchTarget
  PrefetchableComponent.TriggerComponent = memo(function TriggerComponent({ children }) {
    return &lt;PrefetchObserver prefetchQuery={query} &gt;{children}&lt;/PrefetchObserver&gt;;
  });


  return PrefetchableComponent;
}"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createPrefetchableComponent</span>(<span class="hljs-params"> { query, prefetchTarget, options }, Component</span>) </span>{

  <span class="hljs-comment">// Wraps "target" component</span>
  const PrefetchableComponent <span class="hljs-operator">=</span> memo(<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">PrefetchableComponent</span>(<span class="hljs-params">{ variables }</span>) </span>{

    <span class="hljs-comment">// Fetching &amp; refresh methods</span>
    const queryRef <span class="hljs-operator">=</span> useLazyLoadQuery(query, variables, options);
    const { refresh, isRefreshing } <span class="hljs-operator">=</span> useRefreshQuery(query, variables);

    const props <span class="hljs-operator">=</span> { queryRef, refresh, isRefreshing, variables };

    <span class="hljs-keyword">return</span> <span class="hljs-operator">&lt;</span>Component {...props} <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
  });

  <span class="hljs-comment">// Attach the Trigger component with the query &amp; prefetchTarget</span>
  PrefetchableComponent.TriggerComponent <span class="hljs-operator">=</span> memo(<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">TriggerComponent</span>(<span class="hljs-params">{ children }</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-operator">&lt;</span>PrefetchObserver prefetchQuery<span class="hljs-operator">=</span>{query} <span class="hljs-operator">&gt;</span>{children}<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>PrefetchObserver<span class="hljs-operator">&gt;</span>;
  });


  <span class="hljs-keyword">return</span> PrefetchableComponent;
}</code></pre><div data-type="callout" type="info"><link rel="preload" as="image" href="https://paragraph.com/editor/callout/information-icon.png"><div class="callout-base callout-info" data-node-view-wrapper="" style="white-space:normal"><img src="https://paragraph.com/editor/callout/information-icon.png" class="callout-button"><div class="callout-content"><div><p><strong><em>Note:</em></strong><em> This is a heavily simplified snippet, in practice it’s a typescript fiesta.</em></p></div></div></div></div><p>Then use this to wrap our prefetchable components:</p><pre data-type="codeBlock" text="const PrefetchableProfile = createPrefetchableComponent({ 
  query: profileQuery,
  prefetchTarget: 'profile'
}, function Profile({data, refresh}) {
   
   // ...

})"><code><span class="hljs-keyword">const</span> <span class="hljs-title class_">PrefetchableProfile</span> = <span class="hljs-title function_">createPrefetchableComponent</span>({ 
  <span class="hljs-attr">query</span>: profileQuery,
  <span class="hljs-attr">prefetchTarget</span>: <span class="hljs-string">'profile'</span>
}, <span class="hljs-keyword">function</span> <span class="hljs-title function_">Profile</span>(<span class="hljs-params">{data, refresh}</span>) {
   
   <span class="hljs-comment">// ...</span>

})</code></pre><p>And finally around our triggers:</p><pre data-type="codeBlock" text=" function ProfileLink({ children, profileId }) {
  const navigateToProfile = useCallback(() =&gt; {
    navigate('Profile', { profileId });
  }, [profileId]);

  return (
    &lt;PrefetchableProfile.TriggerComponent variables={{profileId}}&gt;
      &lt;Pressable onPress={navigateToProfile}&gt;
        {children}
      &lt;/Pressable&gt;
    &lt;/PrefetchableProfile.TriggerComponent&gt;
  );"><code> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ProfileLink</span>(<span class="hljs-params">{ children, profileId }</span>) </span>{
  const navigateToProfile <span class="hljs-operator">=</span> useCallback(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    navigate(<span class="hljs-string">'Profile'</span>, { profileId });
  }, [profileId]);

  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&lt;</span>PrefetchableProfile.TriggerComponent variables<span class="hljs-operator">=</span>{{profileId}}<span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span>Pressable onPress<span class="hljs-operator">=</span>{navigateToProfile}<span class="hljs-operator">&gt;</span>
        {children}
      <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>Pressable<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>PrefetchableProfile.TriggerComponent&gt;
  );</code></pre><p>Add a couple AI rules and context on top, and implementing prefetching becomes one-shotable.</p><h2 id="h-results" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Results</h2><h4 id="h-smooth-ux" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Smooth UX</h4><p>Previously, navigating between tabs led to a shimmer fest:</p><div data-type="youtube" videoid="W9Cq9H-TOYA">
      <div class="youtube-player" data-id="W9Cq9H-TOYA" style="background-image: url('https://i.ytimg.com/vi/W9Cq9H-TOYA/hqdefault.jpg'); background-size: cover; background-position: center">
        <a href="https://www.youtube.com/watch?v=W9Cq9H-TOYA">
          <img src="https://paragraph.com/editor/youtube/play.png" class="play">
        </a>
      </div></div><p>With prefetching enabled, navigation &amp; rendering is instant:</p><div data-type="youtube" videoid="PDdOG2-2ocM">
      <div class="youtube-player" data-id="PDdOG2-2ocM" style="background-image: url('https://i.ytimg.com/vi/PDdOG2-2ocM/hqdefault.jpg'); background-size: cover; background-position: center">
        <a href="https://www.youtube.com/watch?v=PDdOG2-2ocM">
          <img src="https://paragraph.com/editor/youtube/play.png" class="play">
        </a>
      </div></div><p>We added debugging tools to let us “see” the prefetching in action</p><div data-type="youtube" videoid="6mxZC4wk3ms">
      <div class="youtube-player" data-id="6mxZC4wk3ms" style="background-image: url('https://i.ytimg.com/vi/6mxZC4wk3ms/hqdefault.jpg'); background-size: cover; background-position: center">
        <a href="https://www.youtube.com/watch?v=6mxZC4wk3ms">
          <img src="https://paragraph.com/editor/youtube/play.png" class="play">
        </a>
      </div></div><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a4f95d0d0e414ae954737bc160f6c9c6c6e100979afa355d8102f05b948e5f7f.svg" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAICAIAAAAX52r4AAAACXBIWXMAAAsTAAALEwEAmpwYAAAB8ElEQVR4nI2RQYTjUBzGH70OITZ6qRJWPDOWUFmlrCilx9LTKt1DCSHUHqKEHNYjhOyWEkLtI4SytuZQyhx66mHW6qlG9RBKDjFsCFWx7YS3NLMrs3bY3+n5/v/n8/0/QAj5dn39cDrtdjvf95MkSdM0PkMI2e/35DdBEByPx/2ZP6MoirJpHMfHM4SQbC3TQXB39wKAdyxnmiaEkOO46XRqmibDMJeXV9VqVVEUiqJEUaQo6tUZlmVFUazX6xzHqapK07QgCK1WS1GUYrHI83yhUGg2m48GhJAvlvXzcCDPkKZpkiR/idvtlvwfIIpicPX26813TdNkWcYYr1arxWLhOI7neZZl2bYtSdJyucQYe56HMbYsyzCM0WjU6/UQQqqqIoRc1x2fQQg9Mbj/EQMA3qPPnuchhPr9/nq9Xi6Xtm27rqvr+mQy6XQ68/lc1/XhcGia5mAwcByn2+1myng8tixLkiSMsSzLmqY9MSCE3Pq3p4dTGIZpmuYvE4ZhfjWKonznWZnZl6zbKIoyJX9S4N/74CV4/eFNp9NhWZZhGAjhbDbDGAuCUDsDIazVaizLXlxcGIbRbrcrlQrDMBRF0TRdKpVkWYYQlstlAADP8xDCRqPhOM5jgo/TT4fk2ZLzBEGQD/TPd8Zms8ni/gI0JyBX+9b1IAAAAABJRU5ErkJggg==" nextheight="100" nextwidth="384" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><h4 id="h-performance" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Performance</h4><p>We measure our app’s performance via a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.coinbase.com/blog/performance-vitals-a-unified-scoring-system-to-guide-performance-health-and-prioritization"><u>unified scoring system</u></a> introduced at Coinbase:</p><ul><li><p><strong>NTBT</strong>: Navigation Total Blocking Time</p></li><li><p><strong>ART</strong>: Above the fold Rendering Time</p></li><li><p><strong>TRT</strong>: Total Rendering Time</p></li></ul><p>Together, these metrics capture how quickly users can interact, see initial content, and view the fully loaded screen.</p><p>See performance comparison results on High End devices:</p><p><strong>NTBT: -80-100%<br><br></strong>The data is available in cache, no suspense is triggered and navigation is instant</p><pre data-type="codeBlock" text="| Screen        | Before | After | Diff (%) |
|---------------|--------|-------|----------|
| Search        | 44     | 0     | -100%    |
| Transact      | 183    | 25    | -86.3%   |
| Notifications | 48     | 0     | -100%    |
| Wallet        | 126    | 38    | -69.8%   |
| Profile       | 229    | 0     | -100%    |"><code><span class="hljs-operator">|</span> Screen        <span class="hljs-operator">|</span> Before <span class="hljs-operator">|</span> After <span class="hljs-operator">|</span> Diff (<span class="hljs-operator">%</span>) <span class="hljs-operator">|</span>
<span class="hljs-operator">|</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">|</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">|</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">|</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">|</span>
<span class="hljs-operator">|</span> Search        <span class="hljs-operator">|</span> <span class="hljs-number">44</span>     <span class="hljs-operator">|</span> <span class="hljs-number">0</span>     <span class="hljs-operator">|</span> <span class="hljs-number">-100</span><span class="hljs-operator">%</span>    <span class="hljs-operator">|</span>
<span class="hljs-operator">|</span> Transact      <span class="hljs-operator">|</span> <span class="hljs-number">183</span>    <span class="hljs-operator">|</span> <span class="hljs-number">25</span>    <span class="hljs-operator">|</span> <span class="hljs-number">-86.3</span><span class="hljs-operator">%</span>   <span class="hljs-operator">|</span>
<span class="hljs-operator">|</span> Notifications <span class="hljs-operator">|</span> <span class="hljs-number">48</span>     <span class="hljs-operator">|</span> <span class="hljs-number">0</span>     <span class="hljs-operator">|</span> <span class="hljs-number">-100</span><span class="hljs-operator">%</span>    <span class="hljs-operator">|</span>
<span class="hljs-operator">|</span> Wallet        <span class="hljs-operator">|</span> <span class="hljs-number">126</span>    <span class="hljs-operator">|</span> <span class="hljs-number">38</span>    <span class="hljs-operator">|</span> <span class="hljs-number">-69.8</span><span class="hljs-operator">%</span>   <span class="hljs-operator">|</span>
<span class="hljs-operator">|</span> Profile       <span class="hljs-operator">|</span> <span class="hljs-number">229</span>    <span class="hljs-operator">|</span> <span class="hljs-number">0</span>     <span class="hljs-operator">|</span> <span class="hljs-number">-100</span><span class="hljs-operator">%</span>    <span class="hljs-operator">|</span></code></pre><p><strong>TRT &amp; ART: -70-80%</strong></p><p>We skip the loading steps entirely, the screen renders near-instantly in its final state.</p><pre data-type="codeBlock" text="| Screen        | Before | After | Diff (%) |
|---------------|--------|-------|----------|
| Search        | 283    | 33    | -88.3%   |
| Transact      | 765    | 141   | -81.6%   |
| Notifications | 447    | 127   | -71.6%   |
| Wallet        | 849    | 128   | -84.9%   |
| Profile       | 511    | 94    | -81.6%   |"><code><span class="hljs-operator">|</span> Screen        <span class="hljs-operator">|</span> Before <span class="hljs-operator">|</span> After <span class="hljs-operator">|</span> Diff (<span class="hljs-operator">%</span>) <span class="hljs-operator">|</span>
<span class="hljs-operator">|</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">|</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">|</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">|</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">|</span>
<span class="hljs-operator">|</span> Search        <span class="hljs-operator">|</span> <span class="hljs-number">283</span>    <span class="hljs-operator">|</span> <span class="hljs-number">33</span>    <span class="hljs-operator">|</span> <span class="hljs-number">-88.3</span><span class="hljs-operator">%</span>   <span class="hljs-operator">|</span>
<span class="hljs-operator">|</span> Transact      <span class="hljs-operator">|</span> <span class="hljs-number">765</span>    <span class="hljs-operator">|</span> <span class="hljs-number">141</span>   <span class="hljs-operator">|</span> <span class="hljs-number">-81.6</span><span class="hljs-operator">%</span>   <span class="hljs-operator">|</span>
<span class="hljs-operator">|</span> Notifications <span class="hljs-operator">|</span> <span class="hljs-number">447</span>    <span class="hljs-operator">|</span> <span class="hljs-number">127</span>   <span class="hljs-operator">|</span> <span class="hljs-number">-71.6</span><span class="hljs-operator">%</span>   <span class="hljs-operator">|</span>
<span class="hljs-operator">|</span> Wallet        <span class="hljs-operator">|</span> <span class="hljs-number">849</span>    <span class="hljs-operator">|</span> <span class="hljs-number">128</span>   <span class="hljs-operator">|</span> <span class="hljs-number">-84.9</span><span class="hljs-operator">%</span>   <span class="hljs-operator">|</span>
<span class="hljs-operator">|</span> Profile       <span class="hljs-operator">|</span> <span class="hljs-number">511</span>    <span class="hljs-operator">|</span> <span class="hljs-number">94</span>    <span class="hljs-operator">|</span> <span class="hljs-number">-81.6</span><span class="hljs-operator">%</span>   <span class="hljs-operator">|</span></code></pre><p>We saw similar results on low end devices</p><p><strong>NTBT: -60%<br><br></strong>The data is available in cache, but de-serialization is still expensive on low end devices.<br><br><strong>TRT &amp; ART: -40%</strong></p><p>Rendering is faster but still expensive on low end devices.</p><hr><h1 id="h-try-it-out-today" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Try it out today</h1><p>Download the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://join.base.app/"><u>Base app</u></a> and experience instant, seamless navigation for yourself—no shimmers, no waiting, just the onchain everything app the way it's meant to feel.</p><hr><h2 id="h-appendix-rendering-tech-stack-and-rn-new-architecture" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Appendix: Rendering, Tech Stack &amp; RN New Architecture</h2><p>Curiously, a few screens remained stubbornly slow even with prefetching enabled. What's going on?</p><p>All our investigations pointed to the same conclusion: Our stack was aging.</p><p>Running React Native <strong>0.77.3</strong> in legacy mode meant missing out on bug fixes &amp; new features.</p><p>The New Architecture, its flashy new Fabric UI rendering engine and bridgeless native call should also streamline rendering.</p><p>But more importantly, it blocked us from upgrading third‑party libraries like FlashList V2, Reanimated V4, and many others that have already moved to the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://reactnative.dev/architecture/landing-page"><u>New Architecture</u></a>.<br><br>In theory, it’s just a matter of switching a flag somewhere in the app config:</p><pre data-type="codeBlock" text="-newArchEnabled=true
+newArchEnabled=false"><code><span class="hljs-attr">-newArchEnabled</span>=<span class="hljs-literal">true</span>
+<span class="hljs-attr">newArchEnabled</span>=<span class="hljs-literal">false</span></code></pre><p>Sounds easy enough right?</p><p>Subscribe to the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.base.dev">Base Engineering Blog</a> &amp; stay tuned for more on the New Architecture.</p>]]></content:encoded>
            <author>base-engineering-blog@newsletter.paragraph.com (Léo Galley)</author>
            <category>base-app</category>
            <category>mobile</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/0f74c08b4e1e348abc921ad693f67c1a2b47aa7d7aed8cb4abd45c411be07fe5.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Engineering the Base-Solana Bridge]]></title>
            <link>https://blog.base.dev/engineering-the-base-solana-bridge</link>
            <guid>dCxGUOUpP5eyE3l33Vng</guid>
            <pubDate>Thu, 11 Dec 2025 18:15:07 GMT</pubDate>
            <description><![CDATA[In this post, we dive into the engineering behind the new Base-Solana bridge that powers direct asset and message transfers between the chains.]]></description>
            <content:encoded><![CDATA[<p><strong>TL;DR: </strong>The new Base-Solana bridge is a custom-engineered solution for direct asset and message transfers between the chains. It represents our first major step towards an interconnected multichain global economy.</p><hr><p>We recently launched a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.base.org/base-solana-bridge"><u>bridge between Base and Solana</u></a>, enabling token transfers and arbitrary message passing between the chains. This post details the architecture, the engineering constraints that influenced our design choices, and our roadmap for future improvements and decentralization.</p><h1 id="h-why-build-a-bridge" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Why Build a Bridge?</h1><p>To build a global economy, we need to make it dead simple for people to move assets at the speed of the internet and tap into liquidity across every chain. While the Ethereum ecosystem is already a highly interconnected web of L2s, a canonical connection to chains outside this ecosystem has been missing.</p><p>Our first step is unlocking access to Solana, a network with <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.coingecko.com/en/chains/solana"><u>over $8B TVL and $5B in daily trading volume</u></a>. There are existing bridging options between Base &lt;&gt; Solana operated by unaffiliated third parties, so why invest in building a bridge?</p><p>We believe there is value in solving this problem at the protocol level in Base. Existing third party protocols compete for blockspace in the public mempool, subjecting them to variable costs and latency constraints. Adding native interop within Base allows us to:</p><ul><li><p>Introduce canonical Solana asset representation within Base, and canonical Base asset representation on Solana.</p></li><li><p>Lower costs, as the native bridge does not need to charge a protocol fee outside of covering existing gas costs.</p></li><li><p>Index finalized Solana state in real time for lower latency.</p></li><li><p>Own the security properties affecting cross-chain assets.</p></li></ul><h1 id="h-bridge-design" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Bridge Design</h1><p>We designed the bridge as a low-level primitive, taking inspiration from the native bridge between Base and Ethereum. It is asymmetric by necessity, leveraging the unique strengths and working around the constraints of both chains. Supported actions include token transfers, arbitrary function calls, and combinations of the two.</p><h2 id="h-system-properties" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">System Properties</h2><h3 id="h-token-bridging" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Token Bridging</strong></h3><p>Before bridging a source chain asset, a corresponding token wrapper must be created on the destination chain. These wrappers are tokens owned by the bridge, granting the bridge the exclusive permission to mint and burn them as assets move across the network.</p><p>This mechanism creates a ‘lock and mint’ concept. First, source chain assets are locked in a bridge-owned vault; the bridge then mints an equivalent amount of wrapped tokens on the destination chain for the user. Then to bridge back, the destination chain wrapped tokens are burned, which triggers the unlocking of the original assets from the source vault.</p><h3 id="h-latency" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Latency</strong></h3><p>End-to-end latency is strictly bound by source chain finality and oracle verification overhead.</p><ul><li><p><strong>Solana → Base:</strong> Latency is dictated by Solana's <code>finalized</code> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.solanalabs.com/consensus/commitments"><u>commitment level</u></a>. Messages are not processed until a supermajority of the validator set (66%+) confirms the slot, ensuring reorg protection (approx. 15 seconds).</p></li><li><p><strong>Base → Solana:</strong> Latency is bound by Ethereum L1 finality. State updates are propagated only after the sequencer batch is posted to Ethereum and the L1 block reaches <code>finalized</code> status (approx. 15 minutes).</p></li></ul><h3 id="h-fees" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Fees</strong></h3><p>Cross-chain bridging presents a fundamental resource pricing challenge: executing a transaction on the destination chain requires paying for blockspace in that chain's native asset, but the user initiates the action on the source chain.</p><p>In our existing canonical system, deposits from Ethereum to Base are automatically included by the sequencer, whereas withdrawals from Base to Ethereum require users to manually prove and execute the transaction on L1.</p><p>We designed the Solana bridge to follow this exact pattern:</p><ul><li><p><strong>Solana → Base:</strong> This path functions like a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.optimism.io/op-stack/bridging/cross-domain#deposit-flow"><u>deposit</u></a>. The protocol guarantees the <em>ability</em> to execute on Base by pre-validating the transaction. The bridge collects a fee to cover the cost of this submission. Users can optionally pay for automatic relaying by specifying a gas limit as well. The exact fee collected is derived algorithmically via a mechanism adapted from Ethereum’s <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-1559"><u>EIP-1559</u></a>.</p></li><li><p><strong>Base → Solana:</strong> This path functions like a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.optimism.io/op-stack/bridging/cross-domain#withdrawal-flow"><u>withdrawal</u></a>. The protocol propagates state roots but does not perform execution. Users or third-party solvers are responsible for submitting inclusion proofs and paying the requisite SOL gas fees directly on Solana. No in-protocol fee is required.</p></li></ul><h2 id="h-architecture" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Architecture</h2><p>The bridge relies on two primary onchain deployments: a monolithic <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.anchor-lang.com/docs"><u>Anchor</u></a> program on Solana and a set of modular smart contracts on Base. It also relies on offchain validators operated by Coinbase and Chainlink.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/d105d1752ac4bcb91a9b6a334a1de66893ffb63f67eb80f45ee6edf1f7838fec.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAOCAIAAADBvonlAAAACXBIWXMAAAsTAAALEwEAmpwYAAACI0lEQVR4nK2UoW4cMRCG/QJ5pXuF0NCQgCMFS1ZqAkKWbIHBoqNLliwxsarbAz41JlYlgxpNwRS4wCCu1AxxwVXZSe6up7RRpH5gtfba83tmfq/YvUZKyRijlNJae+9LKbu3IF5dAQBKqa7rhmGw1uac/7NAKYWIUko5ZyJ6U/RHAd7/UtxfOf8gommaAEBKudlsttutc59TSvGJ7w8PP4kozxDR/f39SQ0PGZQZmiml5JxTSqUUnAGAlBIippR4Tc45xrhfz0/e8odAjLHrut1uh4h938cYOSIHjfOQ62OM0VrHGGEGEUMIJ+sBYH9cFhPMYrHgfgJ8NcaEENq2lVKG8MV7z6eepsk5x4EQcRiG99fXSimWRERrrff+tAdnZ2dCiHEcOVki+nR3Z60dhqHrunEcOUQpxXvvnLPPKKXa9oPWej+jtXbOcUdXq5UQ4ubm5tADjsJ5GGOklBcXF8vlsmkaRCyluJnjgvR9773nYYyRBdjHT5URQrAFjwUQv2n9sZq5vb2tqgoRiSjM7PPg0ABgrXXO8Twi8nEBQAjhnHshgxjjer2u6/rq6urdDAAQEQB477uua54xxlhr27ZtmkZKud1u+awv23QvwP+Guq7Pz88vLy+rqmI7sm2steM4tm3LSTjn+Ecy1+/xiuwDsnEPAlwHdnQIQR7BNuWrEEJwzimlQghcIp5hj8YY/5pBzpl7xfBFY3jm+OUfn04EfgPn4YBXIwgJLwAAAABJRU5ErkJggg==" nextheight="616" nextwidth="1370" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-solana-architecture" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Solana Architecture</strong></h3><p>On Solana, we deployed a single Anchor-based <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/bridge/blob/39b6f53a1e2c623bef1dd2fc9626389784d32e4c/solana/programs/bridge/src/lib.rs"><u>bridge program</u></a> as the gateway for all cross-chain activity. This program handles three critical functions.</p><ul><li><p>Message Egress: Creates discrete <code>OutgoingMessage</code> accounts for every action targeting Base.</p></li><li><p>State Verification: Maintains a registry of valid Base roots and verifies inclusion proofs for incoming messages.</p></li><li><p>Asset Custody: Manages <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://solana.com/pt/docs/core/pda"><u>Program Derived Addresses</u></a> (PDAs) that act as vaults for locked SPL tokens and holds the mint authority for wrapped Base assets.</p></li></ul><h3 id="h-base-architecture" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Base Architecture</strong></h3><p>On Base, we chose a modular system to handle EVM state management and execution complexity:</p><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/bridge/blob/39b6f53a1e2c623bef1dd2fc9626389784d32e4c/base/src/Bridge.sol"><u>Bridge</u></a>: The central hub. Handles message ingress/egress, holds locked assets (ETH and ERC20s), and controls minting/burning of wrapped Solana assets.</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/bridge/blob/39b6f53a1e2c623bef1dd2fc9626389784d32e4c/base/src/BridgeValidator.sol"><u>BridgeValidator</u></a>: Verifies batches of messages from Solana before execution.</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/bridge/blob/39b6f53a1e2c623bef1dd2fc9626389784d32e4c/base/src/Twin.sol"><u>Twin</u></a>: A deterministic execution context for Solana users, allowing interactions with Base dApps without managing a separate EVM key.</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/bridge/blob/39b6f53a1e2c623bef1dd2fc9626389784d32e4c/base/src/CrossChainERC20Factory.sol"><u>CrossChainERC20Factory</u></a>: Deploys and manages canonical wrapped representations of Solana tokens.</p></li></ul><h3 id="h-offchain-architecture" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Offchain Architecture</strong></h3><p>We operate a custom indexer to tie the onchain components together. This service monitors the state of both the Solana program and the Base contracts. When a state transition occurs (e.g., an outgoing message is emitted), the oracle:</p><ol><li><p>Indexes the event data from the source chain.</p></li><li><p>Requests signatures from the Chainlink <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://chain.link/education/blockchain-oracles"><u>Decentralized Oracle Network</u></a> (DON) and our internal validators.</p></li><li><p>Relays the signed state to the destination chain.</p></li></ol><h2 id="h-flow-diagrams" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Flow Diagrams</h2><h3 id="h-solana-base-bridging" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Solana → Base Bridging</strong></h3><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/06c9a015e1d96f994fca4296add39a7bb923a8c4ecc99d0de0feea8fac921e70.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAUCAIAAABj86gYAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEfklEQVR4nJWVbW/bVBTH/Ya38A36EXiJBEIIxCQ2QIJJgARIDFGBqrENBt1WVa0Ky7pSxkQ1bdWEGA0Kog+Qdk2jbEnqxLPjxokTJ07iGzu5idukTdMkm1cns+TGrZHjkWZbW9hfR5Yfzv/+7rm2z0U0TVNVVdM0/V+1z3fatx6Vqqq6rnd1dSEIkkgkOi1PCtnzcam0LoqF/TyKohjOlkKh0H8ATJkTUVqamrH3nhs89W3fhNVmTna/OgqFlYNH3wVACM3UbU3zouj3w6OD5y/OL7gOMIviMurHzWoOArSnACHMZDKVarVSrTIxllyi1svl1bWSeadSrZppqroly7Lbg/YPXejuOTlyaQzCnKIockuKokjS/XaoqoqoLWmaRhABDCMWfTgWTDrRoN3pJ8LA5fYzsTgTY+kII8uyruu1Ws2x4HJ70CtXf/nO8sOEbZIK0SkOuFzekdHLdvs8B/hkkksmOSbGlkolowKzCAjzEOZxMkyxeW8gfhuLUEkRxYOCALMwzwFeluu6rkuSxAE+L4psIunDcEGAPA+nZuwffHTs8NtHPz7WPb/ggvllIZtjE6lSaX0XAFo2Jsb68aVoLGlGPJ4CIANApg2QZZkKRVGcwkja6fZjJIP6A0yMnbDaes8NWG2TbCIJAA8A36qgA5CFECfIsSvj54dHUT8mCJBh43bfrCOwcJN0oLivUX9gAnCCpFMilcj/7cI5sYwGokwsvlJYpSMMRtL+IOu9E/aTzC1fUBAyu4A0nxkYsrz73odvvPlOz5dfU6EoEVwavzk+cHXgmuPabdzdqDdMQJiO+gLhQJjFqbgRJM0BnuchHTEG9QRY21+uOQ8ZiKZTHPcIgImxVtvUr7/Z6AhjGJiYC7/lIRfRKIYtESZAkiQ2keR5CACf5jPmUcjmOMBzgL9DBAmKcfsCGEm7F3EIc7sAUVyGMF8srhWLq+QSJQgZI9JZmMmKOXFZzCuKuUSbEGYLhZUshFkIVwrFYnE1EokWi6srhWJeFEVxGYC0aa/Vag8BLe1omraxUbH9OX36bP+8w6koyqYsz3pCNgc56Qr+PovbHOS0i7I5SDzMNZvNVv7G9IzdMvLT1Iy9VrurtSTL9XZ/6wQYoiPMp58ff+GlV/qHLBDmcmJxaNxhmfAMW71fjU72jc0NW72D153Xp30NeXOr2cQJ8uTps5981tP9xXE6EtU0TVGUzgbzOKD1S+fmF1yVatW8LK6V09nlwloFxYMhOlFYq6T4/L17UrvdlkrrqB+v1u7u2yo6tWfzUVXV58cswz+OXPo5xQFd39kxMrc1bXur2dR1fbvl2tO7RwXmOnZuBpJ0/4b1jxOnzgwMXSCIwJMDmZb/VcFjNkmSchCul0qNet34jlkuyAAqnsZCcSou0MkMFRfWyg8X86kBiqKEURSzzwWczlqlqus7yIsnEORV5LkjCPKyEc8eRp45hHS9X64ar0TTtp8OoKoqBCDNMCAabbXSnaN9N5DXvkHe6kcOnTHiSB/yeu/z3ZcbD4xdYc8t9h/3PGNlmIa8IwAAAABJRU5ErkJggg==" nextheight="831" nextwidth="1303" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><ol><li><p>A user calls the <code>Bridge</code> program on Solana.</p><ul><li><p>Note that because the calldata required for an EVM transaction can easily exceed the transaction size limits on Solana (1232 bytes), this can optionally be broken into multiple steps where the calldata is loaded into a temporary buffer account before initiating the bridging flow.</p></li></ul></li><li><p>The program locks funds in a PDA vault and initializes an <code>OutgoingMessage</code> account containing the execution payload.</p></li><li><p>The Base oracle indexes the new <code>OutgoingMessage</code> account.</p></li><li><p>The oracle computes the message hash and requests <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.cyfrin.io/blog/elliptic-curve-digital-signature-algorithm-and-signatures"><u>ECDSA</u></a> signatures from Base and Chainlink validators.</p></li><li><p>The oracle submits the message and validator signatures to the <code>BridgeValidator</code> contract on Base.</p></li><li><p>A relayer or user calls the <code>Bridge</code> contract to execute the message.</p></li><li><p>The <code>Bridge</code> contract verifies pre-validation in <code>BridgeValidator</code> and executes the payload.</p></li></ol><h3 id="h-base-solana-bridging" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Base → Solana Bridging</strong></h3><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/fe1954a2891edde5ff970b33f1185b4d853cdb0b18255700fc1fddd1332c3d26.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAVCAIAAACor3u9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAEzklEQVR4nI2V62/TVhTA/Sew75u0L3v8D/syjU17sA00JFapDCrYeIiXqEYjoBPvFYJoxcqoaKGlhND0QUpN4tZNmpY0r6a1a7t27CZpkjWqS514mqmhtzHhTvXtQoBu3dH5cB/H53ce9jGmmwIhNAwDmlJalLbGWlcAAAhhIBDAMMxqtb75IBKsfAMAePr02ZtGEMJ0OqOqarkl2lZWVmKmQAg1TXsTg23YsAFdi6LY1NzWeL2Z54VSvMB0dO16y46qPTt37yUIUtd1AICmaciRqqoWiyWdzhhGEYBlAEDx+fNXACX+McuJzVsrvt6y7fS5OlQ0YEpeVRuuXtu8taJy5+6H7gFd1wumawBAPJEoL52mad3OvmA48grA4XDY7XZ07RnyuQjyiRkdCh+YhTYMg2G5OVmGEBbNx2R5/uZt24lT5+87+xRFMU/ks79aK7bv2ra9qqm5FQCAnKz2AABgtzsGSK+bGHQRZGe3c4D04jjh7H3oHw1SNMOwHGqPrut9ON5yq+1ozckdu/YdqrbguDuZnGFYrrPbeeaCte5S/UM3IUlxQZDS6QyGOIZRwN2DoclElEuNT6VCk4kwkxjjZsa4GdLr53mBohlUt3wu7x8NCqI0QHrb2u3BcESSEgzL0zQjCNLQ8AhFM4nkDMsKDMvzvLCawTIABDlExTLMdJaZzsbS84KpXHJu0BfgBZFhOQRQVZXnBV4QRSlO0YyL8ODuQYL0kV4/6V3xbhrzvCByvChK02sAqFimxzVsv0909JJRIT3kD/OCSP+TgaqqDMvxgsgLIkUz5KNxKpa500OMjk+HJhPDI35RiqPb1wH95DAVy7DxlQxqas9u2fr95199s+dgtaO7T5Ti5RmUAAzL4/0+j5/qJUbIRxP9voh/NFgOEITYKsDsgSc0mYxyqdBk8k6P+3D18R/3H7l6w9aL96NgF1EPVJWiVwGIwbB8IBSJUgyqTEyMI30lAwCAZ8jX6xrE3Z5AeCIQoXG35wHeHwhTofC4JElsWQYsy3EcT9EMUo7jGZZj2JcnFM3QNDM+Tr1ssmEYiqLIsqzrizRNZ7NZAMCivphTlJVXIhafEhPygvo4/1d2bmFyKpZOz6qqqiiKpj1JJmdsdzsoitZ1Pa/mFVNUUzRNK59FLx4vLLS126p+2n+o2jIWnYAQxlPZny931DTeP37NWXnstz2nbtbUdx22djTcdun6IoSQoujzF69Yas/Unr4w4g+sM+zS6czJX85+/OmXm779DsfdhlFYUPLX75KNNrKla/hSs7O+Fb/RNdxwu//BYGTZ/FZlWe7q7j1z4XLLrfZUKrUOADUjGI7MyfPlmcGVD3iRIMmxaNScZS/QYam8f8zOoqGyDqB80qJ10VzLsnz+4pWKH3Ztr9pr6+h5tgQKxvNyRZbr/w9KrkumRQgLhvFsack3MmKpPd3aekuZnzWPi68lsWb4awNKAgBICsJEICjQK2/IMliytrk2Hmj44mD9J/usH1Wd23SkceOBhn3n2v/tN7UO4Immue7d62xqwm223MpMLmJvfYZh72LYBxj2Doa9jWHvY9h7GPahN8CYeaCK/m+AYRhqLvdnPq/mcoXlAoSwb2jicN3dmnqHqV019Y6j1o7fO73/UaK/AZHtnAVx8Z8GAAAAAElFTkSuQmCC" nextheight="848" nextwidth="1308" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><ol><li><p>A user calls the <code>Bridge</code> contract on Base.</p></li><li><p>The contract appends the encoded message to an onchain <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://commonware.xyz/blogs/mmr"><u>Merkle Mountain Range</u></a> (MMR) data structure.</p><ul><li><p>We chose an MMR because it generates compact inclusion proofs that fit easily within Solana’s transaction size limits. It is also highly efficient to maintain; appending to the structure on Base costs less than 1/10th of a cent, and we won't hit Solana’s size constraints until the tree reaches ~17 billion nodes.</p></li></ul></li><li><p>The Base oracle detects a new MMR root at a finalized block height.</p></li><li><p>The oracle requests ECDSA signatures for the new root from Base and Chainlink validators.</p></li><li><p>The oracle submits the signed root to the <code>bridge</code> program on Solana.</p></li><li><p>A relayer or user generates an inclusion proof against the updated root (via <code>Bridge.generateProof</code>).</p></li><li><p>The relayer or user submits the inclusion proof to Solana.</p></li><li><p>The program verifies the proof and executes the message.</p></li></ol><h1 id="h-security" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Security</h1><p>The bridge was designed with security as a top priority to ensure solvency and safe bridging. While building the bridge, several invariants we viewed as foundational were:</p><ul><li><p><strong>Solana → Base:</strong> Transactions executed on Base match a deposit on Solana</p></li><li><p><strong>Base → Solana:&nbsp;</strong></p><ul><li><p>State roots posted on Solana match those derived on Base</p></li><li><p>Withdrawals proved against state roots match a transaction on Base</p></li></ul></li><li><p>The amount of minted wrapped tokens on any chain cannot exceed the amount of locked tokens on the opposite chain</p></li></ul><p>To increase our confidence in the bridge’s security, we conducted:&nbsp;</p><ul><li><p>Internal audits of the codebase that spanned from the design phase to deployment</p></li><li><p>A threat model and evaluated design trade offs</p></li><li><p>Penetration tests on the infrastructure</p></li><li><p>Extensive external audits by</p><ul><li><p>Cantina for the smart contracts</p></li><li><p>Trail of Bits for the oracle</p></li></ul></li><li><p>Unit tests and end-to-end tests of all important workflows</p></li></ul><p>As the current design involves a centralized oracle, we worked towards mitigating risks produced by this component until decentralization is achieved.</p><p>As explained in the design, one of the novel aspects of this bridge is the use of a Merkle Mountain Range for state roots instead of a traditional Merkle tree. Our own cryptography team worked with our internal auditors to find hash collisions and write a proof that the current implementation is sound.</p><p>We also have several monitors set up to alert us of anomalous behaviour or broken invariants, such as</p><ul><li><p>Unexpected deposits/withdrawals&nbsp;</p></li><li><p>Discrepancies between the locked amount and minted amount of a bridged token</p></li><li><p>Any privileged function call</p></li><li><p>Delayed or failed transactions by the oracle</p></li><li><p>Changes to our bridge partner’s smart contracts or API availability&nbsp;</p></li></ul><p>Runbooks were also created to prepare our team for incident response. These cover escalation paths and playbooks for specific scenarios from on-chain hacks and transaction discrepancies to off-chain oracle outages and partner availability.</p><h3 id="h-key-management" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Key Management</h3><p>Bridge exploits frequently occur due to key compromise, so we implemented a separation of concerns for the private keys involved with the operation of the bridge:</p><ul><li><p><strong>Configuration keys:</strong> Controlled by a multisig for parameter changes and emergency pauses</p></li><li><p><strong>Upgrade keys: </strong>Separate multisig with different owners for contract upgrades</p></li><li><p><strong>Signing keys: </strong>Used to sign messages between Base and Solana. These are split between Coinbase and Chainlink infrastructure, requiring signatures from both parties</p></li></ul><p>Each of these operations have designated independent multisignature accounts with at least a 3-of-5 threshold. In particular, as the signing keys are particularly critical to bridge operations, our partnership with Chainlink ensures that the keys are distributed among separate infrastructure, so compromising either party alone cannot move funds.</p><p>To decrease the risk exposed by these keys, our vision is to decentralize as much as possible so that the trust model is similar to our current bridge between Ethereum and Base, as explained in the next section. </p><h1 id="h-decentralization-roadmap" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Decentralization Roadmap</h1><p>While the distributed validator set gives us good security properties, our preference is to explore cryptographic unlocks enabling a future system secured only by the consensus of Solana + Ethereum.</p><p>The transition to a fully trust-minimized bridge, we’re thinking in three phases, beginning with the current Proof-of-Authority model secured via independent validator attestations. Subsequent phases will detail efforts toward protocol enshrinement, integrating verification logic for improved efficiency, and the long-term goal of the ZK Prove architecture, which seeks to achieve full decentralization by replacing attestations with cryptographic proofs of consensus.</p><h2 id="h-phase-1-proof-of-authority-current-state" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Phase 1: Proof-of-Authority (Current State)</h2><p>We secure Phase 1 with a shared responsibility model requiring independent attestations from two parties: an oracle run by Base and the Chainlink DON.</p><p>The Chainlink DON attestation currently operates with a 3-of-5 multisignature scheme, and our plan is to increase to a 9-of-16 threshold in the near future. Both the Base oracle and the Chainlink DON must attest to a message for successful processing.</p><p>Validator operators maintain two distinct <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://en.bitcoin.it/wiki/Secp256k1"><u>secp256k1</u></a> keypairs to generate ECDSA signatures—one dedicated to Base and another to Solana. We enforce this key isolation to ensure that a compromise of a validator's key on one chain does not put the other chain at risk. For tracking these addresses, Chainlink manages an onchain registry (a smart contract on Base and a program on Solana) where these chain-specific public keys are registered. Our bridge contracts query these registries dynamically to verify signatures.</p><h3 id="h-solana-base-validation" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Solana → Base Validation</strong></h3><p>For transactions moving from Solana to Base, the Base Oracle and Chainlink validators verify message batches before they are relayed to our <code>BridgeValidator</code> contract.<br></p><ol><li><p>The Base oracle calls the Chainlink API with an array of message hashes corresponding to <code>OutgoingMessage</code> accounts on Solana.</p></li><li><p>Validators confirm that every message hash in the batch corresponds to a valid <code>OutgoingMessage</code> account created by the Solana bridge program.</p></li><li><p>Once verified, validators generate an ECDSA signature over the Keccak-256 hash of the tightly packed array of message hashes (<code>keccak256(abi.encodePacked(messageHashes))</code>).</p></li><li><p>The Chainlink API collects the required threshold of signatures and returns them to the Base oracle, which submits the batch onchain in a single transaction.</p></li></ol><h3 id="h-base-solana-validation" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Base → Solana Validation</strong></h3><p>As mentioned above, the process for validating transactions from Base to Solana uses an MMR to represent the state of the bridge, which helps keep proofs under Solana’s transaction size limit.<br></p><ol><li><p>The Base oracle fetches the latest MMR root, the associated Base block number, and the MMR leaf count from the getRoot function on the Base bridge contract.</p></li><li><p>Chainlink validators verify that the proposed Base block number is finalized and that the output root proposed by the oracle matches the onchain state.</p></li><li><p>Validators provide an ECDSA signature over the Keccak-256 hash of the concatenated MMR root, Base block number, and MMR leaf count.</p></li><li><p>The Base oracle collects these signatures and includes them in the <code>register_output_root </code>transaction it submits to the Solana bridge program.</p></li></ol><h2 id="h-phase-2-enshrinement" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Phase 2: Enshrinement</h2><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/babbe83fdca49baf6063983cff0ddb63005beebef22c41afdb0387fd6e6bc70f.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAARCAIAAAAzPjmrAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEm0lEQVR4nJ2Ua0xTZxjHz7IP2xKTOeMyJBtuQrKliYouEBGKyqRoARVLuUltKdIBoUAZk8G4hIumlEG5HGmhlwO0tqVwTrUckAP00NVyaQcFAdMWZlm0fqhL6mV8KsnOApIFYzJwvzx5Prx53/yS/5vnAYhds2K3X6VEnyWRUiPIk+PjBEGsr6/v+AogCMK3yY5XLZNTl4KPfQQAsV+TDIOD7yDYJX8+86jF7Y0VpeKbN1bs9s2zv1++eOlwOFwul3sTl2vV6Vx2Opd/f/TI6Vx2u92AzWbj8YogCDKbzRg2guPjb3V8YsoSTkk8So47QaFHxDF+EshYhTUp35fKlP1/rK6GnYw4GnyMxyuuFzTK5F0wolNrtGqNVqXWIogOGDMYeLximbzLdH/CgBuNJpMBNw5jo6OGrY6iaKtI+qE/iZ7O+cTvS9LxiFPnEsuqBeHxLEZe5dLiHD0hhUnPTLqQRjtHFza0zS8sTE1bXxeGjQI22xw7k3PiZGRoGHmzIpqEbVPTllGD0YBvFIaNdKu0IdEprJxicszlM7FJoZHUJGb2t2eT8n9uWFqc43GLy36o/I4cI6gRdopl8u7udnEnCIraxZ33hjHAbDYHBB7a4793j//efQGfAh8A2bncmVmb0WQy3Z8wmjZyaxVJoy5f+3VyNqegpKSitqSidsw4GRnPpGf96FpxgqBIJu8Sd0ig7p66G/WMRPYVGjOdxrqeV9bZKQEsFqvfQX9gG9m53KrSGgr5PD0+lZWclc3JBUUdF5lcRvb107EpR8Kik9kFnPxyCi2T3yJ96n4CIzrkrl6l3si9XtBUU34jKpwSc5oqapZsCb4IPLRdkJtf0PILeP5MHCnocGlRZWFBsd1uX1tbe/zkyfPnL2ZmbTCMDGOjIyO4xfKbATeKOyQyeVePQglBkEKhAEGQX8+vrKrqUSiWlh5uCMrKq5hsTnIqI/0qm8nmqDXaYWwUuasfvDesHxjUwrDL5Xo9LgRBPFhYuHDxEiuDoVZ3SSS3urslt1WQTCaWyqSNQpAvaK6u41fX8QUC4eLi4sYcWCxWGEYMBlyt6UVRFMfHYUSn16M63R2d7g6KDqk1vdsFNtsDbZ+ysIgilhT2IbV9SE2HpMg6A8JI06GQWOBgKLD/m/eCIiKpaVbL9IbA5/N5vd5Xf71yOBwej8f7Fmtra9vn3Gq13brVIAR5nx2nAl+FAftJe45QZYpKk0kak5BBpqaERV8OP5/KuMadn5t7Y5I9Hs9uFsbq6mMIalf1lWbx2GmcVHpGIiOXOWasN5mkZvPUw6XFhfn5ZacDx3Gb7X8J3O6ncrkU7m/BhlqxoTZsqG3gbpMBl0FQc1U1/2a9sLFVVFPX0Ap2bP3BNsGz12n8BwRBOBwOFEVzCssDQ+OPnkoOjkoNComFunoLeSWHKcx9x2KBA8EB5LQUNnfVtfKGwOfzeXbC6/VaLNZ+GKYmXAEAP+D9z4GPA4H9QVCPSqG4TbvKTWDkUemZNFZBSUXtyvLyu23Tf5OEYUSvR5VKlVqjhRGdUqmCYQRFh2AY1mo1AwN6GIbVGo19c+P+A37UYpCv/ygqAAAAAElFTkSuQmCC" nextheight="676" nextwidth="1239" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Phase 2 improves cost and latency by moving verification logic into the protocol layer. Rather than verifying Solana messages via smart contracts, we modify the Base protocol to natively index Solana data.</p><p>We plan to utilize a component analogous to the Optimism Superchain's <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.optimism.io/chain-operators/reference/components/op-supervisor"><u>op-supervisor</u></a>. This service ingests <code>OutgoingMessage</code> accounts from Solana and forwards them to the execution engine, enabling nodes to verify cross-chain transactions natively.</p><p>However, native indexing introduces a security paradox. Base derives its security from Ethereum via an <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.optimism.io/op-stack/fault-proofs/explainer"><u>optimistic fault proof system</u></a>. This system is capable of verifying state transitions because it has access to all needed inputs on L1 (deposits and blobs). Since L1 has no visibility into Solana's state, it cannot verify the validity of transactions derived from natively indexed Solana data.</p><p>To resolve this, we must make Solana state available on Ethereum. We introduce a <strong>State Prover</strong> service that posts Solana state representations to L1, allowing the fault proof system to verify inclusion proofs against a trusted root.</p><p>Solana's architecture complicates this. Unlike EVM chains, Solana does not include a global state root in its block header. To bridge this gap, we run a sidecar to Solana validator nodes that computes a merklized root of the bridge state. In Phase 2, Chainlink and Base validators attest to this derived root. </p><p>For the Base → Solana path, we integrate <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/op-enclave"><u>op-enclave</u></a>, a relatively small modification to the op-stack that proves state transitions in an AWS Nitro Enclave and outputs the resulting state root. This removes the need for the 7-day challenge period and allows for immediate withdrawals. The State Prover relays these roots to Solana accompanied by the TEE proof, eliminating the need for validator attestations in this direction.</p><p>We intend to modify <code>op-enclave</code> to prove the bridge MMR root directly, rather than the entire Base state root, preserving the existing client-side proof interface.</p><h2 id="h-phase-3-zk-prove" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Phase 3: ZK Prove</h2><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/75721db4e7b8f3e805b8ef4114af87563643cc32d4e2463a2d666006b19428ca.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAANCAIAAABHKvtLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAD00lEQVR4nJ2Tb0wbZRzHH/WVM/6ZumUshiysZELIlrECljIoI1CwKWyM8m92bXEonTKFzZUKowhDUTvooIsTAgeFgb3agrcrvQN6Le0RmHGgpCQMofCiibtlPZb29qaJnAGUYcZ84SffF0+e5Pl9fvnl94CamiuFRcUIgtrthA3DEQQ1Dw2NjI05xscd4+M2DLdhuM/nW119FAgGA8Ggn6b9NE1RDx76H9I0zbIsQRBGI0wQDo9nbvrfeDxzAIJ6Oru6zZbBzdgJJ4aPXtfqNer6pgbtl/Xf9PXdmp6e2bU/Cuw+CHZHRHCFgtPKhCxprFCakVfKMEGVSp16IkOlqoYgAwQZkNvWzQwiqMUyuC44FH04lvsOL0kQeSgmN68Aw0cl4sK9u8L2vrgvPSnz5vcdk1N3Xj4Yp1BeFp0qjOOfEIolyk9UZZV1h1PzF5cW31eUFuVI42MSU+LTyuQfOV2uyamfXS5yYvKODcPBjRs3AQDgDQCeB+AVEHMsliBcAz8YO7u69frv+gdgkiR/m51NFCvKPmu8pNFWar69oP6qurFV9rFGkPsBRVE6XVtzU6soM/tK1Rf6tnYIMgz0wz+ahgw9/TBsAjqdHmzjLU44SU5WlF88ky/NEoovVVTh+MjsrIeTmKNQXs4U5/IFQqFYUvBeSd7Z83uikz2eOQRBnS6X1YZZfkK0zS3nFRdOpue9HR59tarpmrYFtLd3gOfAmwfC9nPCwasvxPF4GD4qLyiNi+FxoxJKij4cHBzy03Tu2U+TsxXHxdJksSxZLFs/ZMvzSyopioJhE3LbiqLDKDpsNltarrVKi+W8eP7XjVoI6gFGI9zc0lZeXnHunPLzGk1nVzcMmyDIoNPpa2vrOzo6zWZLKBRi2TV2B9ZCodCj1dWV5eXfFxZWllfu3VuYn59fXFry+XwURdE0DegNKIryer0Mw1AbbN0EgoFAMPB04Q0l+8vdu2lp6bV1DbV1DepqTcPVpt7eW5uBYZPZbMHxEbD1wL+x1NtL+Hw+hmHonaAoimGYCdIdnyICBxLAnigQdvRIaj6KDrvcE3bCuZn1LdoqR1EPnm7zWTDM4z/X1uY8HnVtY4qoSCAqSBJKJDIljo8iCGrD8GEMQxCUIBxPBH/cv88wj7fnPwUMy7K/zszISsuPpp5OzCrmphe8Kym5WFV3PKf0iCCPm3EmOVvR19f/t4Bl2R1H8Sz+GRHJT8sGr3E2Pnkkh5tWLFdG8k+C1zkvRcRFJ50y9PY9EfwPvF7vmN3udDhJkhx3uQjCQRAOHMfdbjdBOKyodWpq6i9tlDxr1XneHQAAAABJRU5ErkJggg==" nextheight="519" nextwidth="1283" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>We are sharing our current thinking to provide transparency into our long-term roadmap towards fully trust-minimized architecture, though we have not yet committed to specific design choices. We welcome any and all feedback on the following.</p><p>The objective of Phase 3 is to remove the need for validator attestations entirely. Instead, we aim to rely solely on cryptographic proofs of consensus.</p><p>The primary engineering constraint is verifying Solana's consensus rules within the EVM's resource limits. Solana currently operates with 400ms slot times and a large validator set, requiring the verification of thousands of Ed25519 signatures per block. Replicating this logic directly in a solidity smart contract is gas-prohibitive due to the lack of cheap Ed25519 precompiles and the prohibitive cost of calldata for the signatures.</p><p>We are investigating a ZK-based approach to compress this verification. The proposed system would utilize a ZK circuit to ingest Solana block headers and aggregate signatures, outputting a succinct proof of the valid state root.</p><p>For the Base → Solana path, we propose leveraging the output roots Base already posts to Ethereum. Since verifying Ethereum consensus via ZK is a well-understood problem space, we aim to submit Base roots to Solana accompanied by two proofs:</p><ol><li><p><strong>Consensus Proof:</strong> Verifies the Ethereum state root.</p></li><li><p><strong>Storage Proof:</strong> Proves the inclusion of the Base output root within that Ethereum state.</p></li></ol><p>A critical open question is the latency of this path. Under the current optimistic fault proof system, Base output roots on Ethereum are subject to a 7-day challenge period. Waiting for this finality would degrade the bridge experience significantly (15 minutes → 7 days). Thus, this design assumes Base has a future as a zk-rollup where time to finality is far lower.</p><hr><h1 id="h-looking-forward" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Looking Forward</h1><p>The Base-Solana bridge is live on mainnet today. We invite you to learn more in our <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.base.org/base-chain/quickstart/base-solana-bridge"><u>docs</u></a> and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/bridge"><u>Github repo</u></a> and share your feedback.</p><p>We'll continue to push the limits of what is possible. We envision Base as the central hub of the global economy, with native ability to interop with all major chains–Solana is only the beginning. If you're interested in working on the hardest problems in the industry, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.base.org/jobs"><u>we're hiring</u></a>, and we'd love to hear from you.</p><hr><p>Follow us on social to stay up to date with the latest: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/base"><u>X</u></a> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/i/lists/1876354838289932418"><u>Base team on X</u></a>) | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://warpcast.com/base/"><u>Farcaster</u></a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://discord.com/invite/buildonbase?utm_source=dotorg&amp;utm_medium=nav"><u>Discord</u></a><u> </u></p>]]></content:encoded>
            <author>base-engineering-blog@newsletter.paragraph.com (Jack Chuma)</author>
            <category>bridging</category>
            <category>decentralization</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/ae884515e6c02a4ceff95c02c70f954e07ab7a23fd0262c05e0ead1d9f0a2170.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[How Base Pay Delivers 1-Step Crypto Payments
]]></title>
            <link>https://blog.base.dev/1-step-crypto-payments</link>
            <guid>g3oKwwlMgctP3HkvO7FV</guid>
            <pubDate>Tue, 09 Dec 2025 16:36:25 GMT</pubDate>
            <description><![CDATA[Introducing a new standard for one-step crypto payments, powered by open standards that anyone can adopt. Try it on Shopify today using Base Pay, and start building with one-step payments.]]></description>
            <content:encoded><![CDATA[<p><strong>TL;DR:</strong> Introducing a new standard for one-step crypto payments. Try it on Shopify today using Base Pay <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://coinbaseshop.com/"><u>here</u></a>, and start building with it <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://base.github.io/account-sdk/prolink-playground"><u>here</u></a>.</p><hr><p>Payments are one of the most exciting areas in crypto today, but the actual experience of paying with crypto leaves a lot to be desired. Generally, it is a two to three step process, requiring a user to switch context between their wallet and the app where they are paying multiple times:</p><ol><li><p>Connect wallet</p></li><li><p>Switch network (if needed)</p></li><li><p>Sign&nbsp;</p></li></ol><p>Crypto payments are constrained by wallet APIs and UX patterns that were built for a different era: one in which users were connecting a wallet to an app for a long lived session. For onchain payments to succeed at scale, we must provide something far simpler.&nbsp;</p><p><strong>Today, we're excited to introduce a new standard for one-step crypto payments.</strong> It's built on open standards that are available to anyone, ready to be adopted industry-wide. Try it on Shopify today using Base Pay <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://coinbaseshop.com/"><u>here</u></a>, and start building with it <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://base.github.io/account-sdk/prolink-playground"><u>here</u></a>.</p><p>There are three innovations powering this experience:</p><ol><li><p>Prolinks</p></li><li><p>Stateless Interactions</p></li><li><p>Callbacks</p></li></ol><p>Let's dive into each.</p><h2 id="h-1-prolinks" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">1. Prolinks</h2><p>Prolinks are a new standard (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/ethereum/ERCs/pull/1261"><u>ERC-8050</u></a>) for representing JSON-RPC requests in a highly compressed URI.</p><p>There have been several previous attempts to standardize a URI format for wallet requests. However, past ERCs have been too opinionated, e.g. assuming that the URI represents a transaction.&nbsp;</p><p>Prolinks are generic: allowing communication about any existing or future wallet APIs. They are also size efficient, leveraging protocol buffers and Brotli compression.&nbsp;</p><h2 id="h-2-stateless-interactions" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">2. Stateless Interactions</h2><p>This simplified payments flow also requires wallet APIs that can function without an existing app connection. Historically, wallets will reject API calls like eth_sendTransaction if the user does not have an existing connection to the app. For one-time interactions like payments, this leads to a very cumbersome experience of having to connect and pay as separate steps.&nbsp;</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-5792"><u>EIP-5792</u></a>’s wallet_sendCalls dropped the requirement of a <code>from</code> address, allowing apps to make transaction requests without knowing anything about the user. This leads to better experiences and is also more privacy friendly.&nbsp;</p><p>wallet_sign (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-7871"><u>ERC-7871</u></a>), used by Base Pay on Shopify, works the same way; users can skip the unnecessary setup and pay immediately</p><h2 id="h-3-callbacks" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">3. Callbacks</h2><p>Finally, the wallet needs a way to share info (e.g. status updates, transaction hash, signed data, etc.) back to the app. The web2 world has a time-tested solution: callbacks.&nbsp;</p><p>We bring callbacks into the onchain world via a new EIP-5792-compliant capability: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/ethereum/ERCs/pull/1216"><u>data callback</u></a>. (This is the same capability powering our <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.base.org/base-account/reference/core/capabilities/datacallback"><u>Profile feature</u></a> for Base Account!)&nbsp;</p><h2 id="h-start-building-with-base-pay" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Start building with Base Pay&nbsp;</h2><p><strong>Try the new Base Pay experience </strong><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://coinbaseshop.com/"><strong><u>here</u></strong></a><strong>, and start building with the developer playground </strong><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://base.github.io/account-sdk/prolink-playground"><strong><u>here</u></strong></a><strong>.</strong>  We’re excited to collaborate with other wallets and payment providers to see these standards improved and adopted.</p><hr><p>Follow us on social to stay up to date with the latest: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/base"><u>X</u></a> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/i/lists/1876354838289932418"><u>Base team on X</u></a>) | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://base.app/profile/base?tab=posts"><u>Base App</u></a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://discord.com/invite/buildonbase?utm_source=dotorg&amp;utm_medium=nav"><u>Discord</u></a></p>]]></content:encoded>
            <author>base-engineering-blog@newsletter.paragraph.com (Wilson Cusack)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/e6dba3f5c3cf4dcff7a418d37062abe0e376756b0fab21dfd9a57a15ef5e12ba.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[L1 Upgrades: The Glamsterdam proposals we’re most excited about]]></title>
            <link>https://blog.base.dev/glamsterdam-proposals</link>
            <guid>Ebh94hE0wPI1r63fJefg</guid>
            <pubDate>Tue, 18 Nov 2025 19:37:28 GMT</pubDate>
            <description><![CDATA[In this post,  we dive into the EIPs of the upcoming L1 Glamsterdam upgrade that are most exciting for the Base Chain]]></description>
            <content:encoded><![CDATA[<br><p><strong><em>TLDR: With the Fusaka upgrade scheduled to go live on Ethereum mainnet on December 3, the core dev community has now shifted their attention to deciding which EIPs to include in the next upgrade: Glamsterdam. The Base engineering team is most excited by six EIPs that fall into three categories: blob scaling (</em></strong><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-8070"><strong><em><u>EIP-8070</u></em></strong></a><strong><em>), gas repricing (</em></strong><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-2780"><strong><em><u>EIP-2780</u></em></strong></a><strong><em> and </em></strong><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-8032"><strong><em><u>EIP-8032</u></em></strong></a><strong><em>), and builder UX improvements (</em></strong><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-5920"><strong><em><u>EIP-5920</u></em></strong></a><strong><em>, </em></strong><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-7907"><strong><em><u>EIP-7907</u></em></strong></a><strong><em>, and </em></strong><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-8024"><strong><em><u>EIP-8024</u></em></strong></a><strong><em>).</em></strong></p><p>Upgrades to the Ethereum L1 can have major benefits for users and builders on Base. For example, the Fusaka upgrade will <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/buildonbase/status/1945196759346540781?s=20"><u>enable an 8x increase of blobs</u></a> on a scheduled and safe basis via <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-7594"><u>PeerDAS</u></a> and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-7892"><u>BPO hardforks</u></a>. This increase in blob capacity will provide Base and other L2s with the data availability (DA) capacity necessary to continue to scale and support more users and builders coming onchain.&nbsp;</p><p>The next Ethereum upgrade, Glamsterdam, is expected to hit mainnet in 2026. The headliner EIPs for Glamsterdam are <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-7732"><u>EIP-7732: Enshrined Proposer-Builder Separation</u></a> (ePBS) and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-7928"><u>EIP-7928: Block-Level Access Lists</u></a>, which will support increased blob capacity and more efficient execution performance, respectively. The Ethereum core dev community is now in the process of submitting their support for the non-headliner EIPs they would like to see included.&nbsp;</p><p>From the list of nearly 50 non-headliner EIPs proposed for inclusion, we have boiled our list down to six that we see as most promising. The EIPs we are most excited about fall into three categories: blob scaling, gas repricing, and builder UX improvements. It’s important to note that exclusion from the list below does not mean we are necessarily against any EIP; however, we do share the concern raised by EthPandaOps and others during the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/ethereum/pm/issues/1790"><u>November 13 All Core Devs call</u></a> that if <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-7805"><u>EIP-7805: Fork-Choice Inclusion Lists (FOCIL)</u></a> is included in Glamsterdam, the complexity and unknowns of testing it alongside ePBS could delay the upgrade beyond 2026. While we do not think we should introduce further complexity and risk to Glamsterdam by including FOCIL, the censorship resistance properties FOCIL will bring to Ethereum are important for all users and we hope FOCIL will be prioritized in a future upgrade.&nbsp;</p><hr><h2 id="h-blob-scaling" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Blob scaling</h2><p>As we covered above and in our <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.base.dev/scaling-base-doubling-capacity-in-30-days"><u>scaling blog</u></a> from last month, blobs play an important role in scaling the Ethereum ecosystem. When blob usage is consistently saturated, L1 DA can become a bottleneck for L2s such as Base to support more onchain activity.&nbsp;</p><p>Both PeerDAS and Glamsterdam’s consensus layer (CL) headliner, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-7732"><u>EIP-7732</u></a>, will support an increasing blob target; however, as the blob target begins to grow beyond 48/block, the execution layer (EL) blobpool is expected to become the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-8070#motivation"><u>primary consumer of a given node’s bandwidth</u></a>. <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-8070"><u>EIP-8070</u></a> will address these bandwidth concerns.</p><h4 id="h-eip-8070-sparse-blobpool-spec" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">EIP-8070: Sparse Blobpool (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-8070"><u>spec</u></a>)</h4><p><strong>What is EIP-8070?</strong></p><p>Similar to how PeerDAS allows for sampling of blob data on the CL, the Sparse Blobpool EIP will allow for cell-level, custody-aligned sampling on the EL. This EIP would reduce the probability of an EL node sampling a full blob payload to p = 0.15. This means EL nodes have a p = 0.85 probability of sampling the same custody assignment as their CL counterpart and downloading as little as 1/8 of the blob data. Altogether, this is expected to reduce the average bandwidth consumption of the blobpool by ~4x.</p><p><strong>Why is EIP-8070 important?</strong></p><p>Ensuring that both the CL <em>and</em> EL can support higher blob targets is important for Base and Ethereum to continue to scale and support more usage. As stated by the EIP’s authors, the EL blobpool <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-8070#motivation"><u>dominated average bandwidth consumption</u></a> on full nodes in Fusaka devnets with high blob targets. This raises the concern that block and attestation propagation will not have enough bandwidth left to consume. If block and attestation propagation lack proper bandwidth, network liveness and stability could be put at risk, which would make raising the blob target any further a nonstarter.&nbsp;</p><hr><h2 id="h-gas-repricing" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Gas repricing</h2><p>Gas mispricings are one of the biggest issues for scaling Base today, since execution client performance is bottlenecked by needing to efficiently build any block. A block full of the most mispriced opcodes can create major strain on performance due to the requirement for sequencer and validator nodes to be able to handle the worst-case. These worst-case blocks greatly limit how high we can scale.&nbsp;</p><p>Glamsterdam’s gas repricings intend to more accurately set gas costs with the goal of reducing the impact of scaling bottlenecks created by mispricings and inefficient gas models. These repricings are organized in a meta-EIP: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-8007"><u>EIP-8007</u></a>. While there are many interesting proposals in the EIP-8007 directory, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-2780"><u>EIP-2780</u></a> and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-8032"><u>EIP-8032</u></a> strike us as the most impactful for Base Chain.</p><h4 id="h-eip-2780-reduce-intrinsic-transaction-gas-spec" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">EIP-2780: Reduce intrinsic transaction gas (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-2780"><u>spec</u></a>)</h4><p><strong>What is EIP-2780?</strong></p><p>As its title suggests, EIP-2780 reduces the intrinsic base cost of a transaction from 21,000 to 4,500, which would make the gas cost of a simple ETH transfer 6,000. In addition, this EIP introduces <em>GAS_NEW_ACCOUNT</em> which charges an extra 25,000 when a transfer targets an empty account (i.e. more accurately charges for the state growth caused by account creation) and refines gas accounting around cold/warm account reading and writing.&nbsp;</p><p><strong>Why is EIP-2780 important?</strong></p><p>EIP-2780 will make simple ETH transfers less expensive for Base users in addition to allowing for more of them in a given block. In addition, the <em>GAS_NEW_ACCOUNT</em> portion of this EIP will allow for more accurately priced account creation on Base Chain (assuming its value can be tweaked at the L2 level), reducing potential scaling bottlenecks.</p><h4 id="h-eip-8032-size-based-storage-gas-pricing-spec" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">EIP-8032: Size-Based Storage Gas Pricing (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-8032"><u>spec</u></a>)</h4><p><strong>What is EIP-8032?</strong></p><p>EIP-8032 intends to help with state bloat concerns by dynamically pricing SSTORE operations based on a given contract’s storage size. In doing so, this EIP more accurately aligns storage write costs with the work EVM clients perform. In addition, this EIP uses an activation threshold parameter to ensure that the progressive increases to storage writes only impact extremely large contracts.&nbsp;</p><p><strong>Why is EIP-8032 important?</strong></p><p>The current gas pricing model is not accurately tuned for contracts with large storage size, leaving both Ethereum mainnet and Base susceptible to state bloat often caused by contracts intended for low-cost data anchoring and spam. These types of contracts increase state root calculation time, creating a scaling bottleneck that penalizes normal usage of our network. By adjusting the gas pricing model to capture the O(log(n)) cost of storage root calculation, Base can continue to scale for normal usage by builders and users.</p><hr><h2 id="h-builder-ux-improvements" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Builder UX improvements</h2><p>Builders are foundational to Base. With this in mind, it is important to us to ensure our builders have a secure and smooth experience building on Base Chain. <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-5920"><u>EIP-5920</u></a>, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-7907"><u>EIP-7907</u></a>, and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-8024"><u>EIP-8024</u></a> will help improve this experience.</p><h4 id="h-eip-5920-pay-opcode-spec" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">EIP-5920: PAY opcode (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-5920"><u>spec</u></a>)</h4><p><strong>What is EIP-5920?</strong></p><p>EIP-5920 introduces a new <em>PAY</em> opcode that can be used for ETH transfers without the need to call into the recipient address.&nbsp;</p><p><strong>Why is EIP-5920 important?</strong></p><p>This will benefit builders who need to transfer ETH to their contracts by removing the need to call into the recipient address and transfer execution context to that address. This change will remove a major reentrancy vector and reduce security considerations for our builders.&nbsp;</p><h4 id="h-eip-7907-meter-contract-code-size-and-increase-limit-spec" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">EIP-7907: Meter Contract Code Size and Increase Limit (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-7907"><u>spec</u></a>)</h4><p><strong>What is EIP-7907?</strong></p><p>EIP-7907 increases both the contract code size limit (from 24KB to 48KB) and the initcode size limit (from 48KB to 96KB). In addition, it introduces gas metering (4 gas per 32 bytes above 24KB) to prevent a potential DoS vector via these larger contracts.</p><p><strong>Why is EIP-7907 important?</strong></p><p>EIP-7907 would allow our builders to deploy more complex logic in a single contract. Our builders have given us feedback that the current code size limit is too restrictive and requires them to break their more complex contracts into multiple smaller contracts. This practice introduces additional technical complexity and is also gas inefficient due to the necessity of cross-contract calls.</p><h4 id="h-eip-8024-backward-compatible-swapn-dupn-exchange-spec" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">EIP-8024: Backward compatible SWAPN, DUPN, EXCHANGE (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-8024"><u>spec</u></a>)</h4><p><strong>What is EIP-8024?</strong></p><p>EIP-8024 introduces three new instructions: <em>SWAPN</em>, <em>DUPN</em>, and <em>EXCHANGE</em>. These instructions allow compilers to access deep stack items below the depth of 16 currently supported by the <em>SWAP*</em> and <em>DUP*</em> instructions.</p><p><strong>Why is EIP-8024 important?</strong></p><p>Once these new instructions are supported in solidity compilers, they will reduce the occurrence of stack too deep errors, which in turn will relieve friction for smart contract developers and make smart contract development more approachable for newer Solidity developers.&nbsp;</p><hr><h2 id="h-conclusion" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Conclusion</h2><p>Base is excited to see Ethereum continue to improve with next year’s Glamsterdam upgrade. We believe <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-8070"><u>EIP-8070</u></a>, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-2780"><u>EIP-2780</u></a>, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-8032"><u>EIP-8032</u></a>, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-5920"><u>EIP-5920</u></a>, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-7907"><u>EIP-7907</u></a>, and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-8024"><u>EIP-8024</u></a> are the most impactful for our users and builders.&nbsp;</p><p>If you are a Base user or builder and have other EIPs you think would be impactful to you, please let us know and show your support on the relevant <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ethereum-magicians.org/"><u>Ethereum Magicians</u></a> thread. Another resource we recommend checking out is <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://forkcast.org/"><u>Forkcast</u></a>, a great tool developed by the Ethereum Foundation to provide an overview of a given upgrade’s EIPs and the Ethereum upgrade process more generally.</p>]]></content:encoded>
            <author>base-engineering-blog@newsletter.paragraph.com (Matt Beuttenmuller)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/c6a48868076ea75e50a5687142bf90ea2c7917baa268d1cace1783ab909a28ea.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Scaling Base safely with end-to-end benchmarking]]></title>
            <link>https://blog.base.dev/scaling-base-with-benchmarking</link>
            <guid>m7yYX5jNPu3g4PwAqpkV</guid>
            <pubDate>Thu, 13 Nov 2025 17:48:25 GMT</pubDate>
            <description><![CDATA[A deep dive into our end-to-end benchmarking framework built to test Ethereum client performance under real-world conditions, enabling Base to more safely scale throughput.]]></description>
            <content:encoded><![CDATA[<p><strong>TL;DR: </strong>To scale Base Chain safely, we built an end-to-end benchmarking framework to test Ethereum client performance under real-world conditions. These benchmarks help us identify bottlenecks, validate hardware configurations, and ensure Base can continue to scale reliably while maintaining sub-cent, sub-second transactions.</p><p>As Base grows, we’re seeing more onchain activity than ever before, with an all-time high of <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/WilsonCusack/status/1987902566223827401"><u>15.4M+ transactions</u></a> recorded this week. To meet growing demand, we’re significantly investing in scaling efforts: &nbsp; for example, this month alone we’ve <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.base.dev/scaling-base-doubling-capacity-in-30-days"><u>doubled Base Chain’s capacity</u></a>.</p><p>To scale safely, we must anticipate any performance challenges that could arise <em>ahead of time</em> so we can keep growing without bottlenecks. To accomplish this, we built <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/benchmark"><u>Base Benchmark</u></a>: a new Ethereum L2 benchmarking tool, which simulates running the chain at higher loads to identify bottlenecks in the execution client.</p><h2 id="h-why-did-we-build-base-benchmark" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Why did we build Base Benchmark?</strong></h2><p>The biggest scaling challenge we face today is execution speed. Many variables can affect execution speed, such as machine size, available cache, and client implementation. Additionally, transaction patterns vary widely across blocks, so a hardware setup that works perfectly for one CPU-bound workload may not work as well for a storage-bound workload. Also, different precompiles, storage operations, and transaction types can lead to variable performance across clients.&nbsp;</p><p>When we increase Base Chain’s gas limit, we need to ensure the network continues to run smoothly and transactions are processed quickly for everyone. This means ensuring that the gas limit we plan to scale to is safe across all traffic types and patterns, and that we have a good understanding of the recommended hardware configurations for nodes. As we scale, the chain must remain resilient to processing spam and other resource intensive transactions.</p><p>We needed a way to test the worst case blocks under various conditions. Unfortunately, testnets aren’t sufficient for simulating worst case conditions because state size (the size of all accounts/contract data deployed on the blockchain) has a huge impact on storage performance, and testnets generally don’t have the same state size as mainnet.</p><p>We considered various alternatives, but none offered the automation and flexibility that we wanted in a blockchain performance testing tool.</p><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/ethpandaops/spamoor"><u>Spamoor</u></a>, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/flashbots/contender"><u>contender</u></a>, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/MariusVanDerWijden/tx-fuzz"><u>tx-fuzz</u></a>: These tools allow generating a transaction workload, but don’t handle setting up the network and collecting metrics. We designed Base Benchmark to allow running these as external transaction generators.&nbsp;</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/kurtosis-tech/kurtosis"><u>Kurtosis</u></a>: This sets up an L1 blockchain to run transaction spammers against, but we wanted an automated and lightweight mechanism to setup and teardown hundreds of test chains quickly.</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://cperezz.github.io/bloatnet-website/index.html"><u>Bloatnet</u></a>: This is a public test network with a large amount of state, allowing storage opcodes to be more accurately tested. However, we want to test with an actual Base Mainnet snapshot. Results weren’t ready as of the time of this blog post.</p></li></ul><p><br>Since no existing tools fulfilled all the requirements enabling us to scale safely and efficiently, we built our own tool. </p><h2 id="h-base-benchmark" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Base Benchmark</strong></h2><p>Base Benchmark is our tool for testing Ethereum client performance against various transaction workloads.</p><p>With Base Benchmark, a user defines a benchmark suite to run. We allow testing various gas targets, transaction payloads, clients, and tuning options. Each combination of these runs a block builder that accepts transactions from RPC and builds blocks using the engine API, along with a verifier which accepts built blocks and applies them to the chain state.</p><p>Importantly, Base Benchmark also supports running benchmarks from a snapshot, building blocks on top of an existing chain like Base Mainnet. This allows us to quickly test storage operations against a database with a large state size.</p><p>Base Benchmark helps client developers and anyone who runs L2 nodes measure performance. It can answer questions like:</p><ul><li><p>How fast can the sequencer build blocks?</p></li><li><p>How fast can validators validate those blocks?</p></li><li><p>How fast can we prove execution of a block?</p></li><li><p>What are the slowest precompiles/opcodes in Reth?</p></li><li><p>How much performance gain will I see after a specific optimization is made?</p></li></ul><p>Let’s take a look at some of the ways Base Benchmark can be used to measure and compare client performance.</p><h3 id="h-use-case-1-comparing-performance-of-ethereum-clients" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Use Case 1: Comparing performance of Ethereum clients</strong></h3><p>Base is in the process of moving from mostly running Geth to mostly running Reth. Unlike Geth, Reth syncs blocks in stages, and stores less data, which makes it faster in many cases.</p><p>To compare client speeds we ran a simple benchmark on different cloud instance types, using a chain with no state and mimicking the opcode and storage distribution of Base mainnet. The benchmark tool will run a benchmark for each combination of parameters. This was intended as an approximate simulation rather than an&nbsp; accurate comparison. The results for one of the transaction payloads is shown below.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/4d8ec83ef72671755d84b6d41eb91e06f06f319d592a71206599f17f4a2d36d6.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAXCAIAAADlZ9q2AAAACXBIWXMAAAsTAAALEwEAmpwYAAAEiklEQVR4nLVVTW7cNhSeg/gK2WfhG+QGXnqXA/QQvU1QIE0nU6NwNl05LWq4cTxxZjL6sSRKJCWSj3z8EQtKMx6nLdpF0YcPxOMTxY98fB+5WCwWl8vlu3fvrq6urj8m22w219cfr/5iP/8b0piDvX///vLycrFYLM6/+bZYfyge1h1lTdO0pAGlWtJku12eZY8o8rwsin9GnmfNZFVV1XVdFMX5+fni7OxMKRVjdM4JKWhHecc444xx2lFGGaWsazslFaI1k6FBcwAeYIwZ+sFa65w3BmOMSqmXL18mgkFIBfbjent3t637IQNegUDrDTptHKDbOya12jiljz4coI0zGOaRGn2Mkffi7OxsTyAUXt9+vrn9vP5S3m6Lu225zZtt0ewKsiua5OdNVpJt3hRVt8lSd5PVuyJFNnm9zcm2IL0AbVxRddu8Vib0TwncGFsmaA+UScpkR4XSFjAtH9BpvV/vDLTeuoT9eg+wLlgf0HowLsZI+fDixYtE0A8yxmhdcD7Y9HOwzqM9wvlgXRAYB4y9jpvKfsr17Q4eqFMuDjoyF3g0931zQ/IPtCw0wxjzqjp99mxPIMFWDWupeCSw6QyCsd5YT7pBgtk1sC5FLeNd1q9zUfC4I+ZTmZxC4QdaZZZnlv9SfrmXbRddXtd7gmGQxo4tTSmSYEA7qRA0eh9n9ABgLRNIBYKNhOuOa7CRK1sS2SsvbShYxxFUdAWlRA4hxqpuns0EQijA0HRDS0VNaNP1VcNAm3iwzoH06JyzNiUXrQ0hTJXt58g4jgoMgBnH0Rjnp69HgkFIdJFyxQagvRpEavVUy7Mxp5VPJW7dnmCewlqHRwINOhGANjP9VzuwPlIuGVeUS9or2kuX1jGGMaF3RjmL6BDdOEbE9DE5dh/xISjQShvvA0zt1zuQoMDerjc3v68rQnem30ma9GV9ko9x04Enuc1SMnPtTvV6iCd/6gYwTigcnxL0qUbc5129yZOyJomRrCBZ2eYPbVa22ymeVd2uJGXVZSXJH0hWkuKhnZQ4DyZCarRjElpWA47lkzNQzseypnXLm7av274mXCidtKZRaSvBSLCTkyCUHYThwgiFKaKmuLbKoNIG0ALaGGNZ1ccdxJjyGMLofHA+Op909wg/xbmODGJv4v2DWWf6U25K6pP0TOxcaCPcC3LT5tck+6I7/SehDRKLqmva/qmAp4wHg4F0qdC2tborBpKExm8z3oi4I/pmSzPqSoW/1XmO4iGoX/PdmjdddOVRaEICBkKHjslBgALDBi0VjmNMhRRGpkEiKuMH7bSPg3ZSe+2iMp5LBDtqHzopuAYYHQXZo/6bMmW9YAN0TDAuOybmAp+NOVDBOudwSi7iUWho7V4HCmeh6XQF+K8JZHpOmm5IILylfUM4QFLyOBlBOVitDc7qS5eyS1Po9PakSAiBD+lCCGGcSmYiqA4EAJAGpekSwoT/bpTS09PTxfOT569fv1mtLt6+/XG1urhY/ZSai4vlcvXmgEf/aXDuPv00+8vl6vsf3i6XF69efXdycpLe/f/V/gAiY2CfQ+WicQAAAABJRU5ErkJggg==" nextheight="1134" nextwidth="1600" class="image-node embed"><figcaption htmlattributes="[object Object]" class=""><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://meyer9.github.io/base-benchmark-results/client-comparison/#/run-comparison/latest?filters=eyJwYXJhbXMiOnsicm9sZSI6InZhbGlkYXRvciIsIkdhc0xpbWl0IjoiMTAwMDAwMDAwIn0sImJ5TWV0cmljIjoiTm9kZVR5cGUifQ%3D%3D"><u>Link to Results</u></a></figcaption></figure><p>The results page is a great starting point for understanding overall performance and notice any issues that warrant deeper investigation.</p><p>For each test case, the benchmark tool first builds blocks by sending transactions to a sequencer. We measure how fast the sequencer can accept incoming transactions. After a certain gas limit, the sequencer stops accepting transactions and builds smaller blocks. This helps us understand the limits of our sequencer under conditions very close to what we would see on Base Mainnet.<br><br>After sequencing the blocks, the blocks are then tested for syncing speed by sending them to a separate validator node. The validator node has to do the work of executing the block, but can skip the mempool work that the sequencer already did. This helps us test how fast external nodes other than the sequencer can process transactions. We run our sequencer on a large server to be able to handle the sequencing workload, but we don’t expect everyone to run Base on such a large server.&nbsp;</p><p><span data-name="bulb" class="emoji" data-type="emoji">💡</span><em>This testing is critical to ensure we scale with accurate measurements of the limitations of validator nodes run by teams outside Base.</em></p><p>In this test, Reth outperforms Geth. The actual benchmarks we use are more complicated and still in development, but this shows how the tool can be used to compare Ethereum L2 client implementations.</p><h3 id="h-use-case-2-comparing-machine-instance-types" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Use Case 2: Comparing machine instance types</strong></h3><p>Another question we often get is: what is the optimal node configuration for my validator node? Base Benchmark helps answer this by providing a consistent workload to benchmark against.</p><p>For this test, we ran our suite of tests against two different instance types: AWS i7ie.48xlarge and GCP z3-highmem-88-highlssd. We can easily compare the metrics of each instance.</p><p>Again, this benchmark run is not necessarily indicative of real-world performance, but provides a good relative testing setup for investigating performance issues in clients.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/553ff83b59aa3988bcbc5ac810e03aee4ba391f5b8fd708737cc63a9ba33e6e5.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAUCAIAAABj86gYAAAACXBIWXMAAAsTAAALEwEAmpwYAAADpklEQVR4nLVU224jNwzNn/of6g/wB7TPBfI1AfpUoFh063UQZzMZO02NzMWe0Y0UdS84Y2eTYLF96gEhSBTFQ5GUrq6vr9e///bp86f9fn84HIa+H4T4+vh49xZfv75bflN/1G82m/V6vdlsbm9v1+v1zc3N1U+rX8zwHDIA2L7rpBAI0HVd2zRt03ZtO02aedK8NO158tLywCMbt+2x78dh0EpZa5VS3nut9Wq1uvr152U3lkEXrcTpeOy7bhwH75x3DM9CztGseSvzLhGpGVIqKQEArXXOlVKcc8vlkgkOR3/SRUohpBJCagOllJRKKiwx/UhCTAbQGFQatQGlDaD1PpRSEIgJlsuV1KzW4AzSIAz5MEgYFUqNgEQu/FhcyD4k56Pz0ccySPj70PWDFhJngqXUzA+AgLbre7SklFEa+UJShXg+PEmY5HUZfUhEAZGcCz6wAVqnNLqQpb4QGMApJ2keybnD4VDX9dPTU9d1RJRSyjmXnGOIMcZSSp4QQrBETdPcb7dt2xLRvDvDwIVAG7DWeu9ngpRSjDGEyVmMs/ecSwk2xWgtEdFMMFuexxhTLsXpEugDwYqLo5SfSs+eUijs74I8u+fDOUYhuWVeN1Muk/cQU/LBezMmyz368QbGmFnLwKEEThojYHGTu5yKMzmXYRj7vregrZGoBWqFRlrQqAWhct6jJdTiOwQgBSKgkXzASEIgBGvEJMajJDS5FG738TSlK6QYck6lvLnuG3wjMIAhBK01Z5fIkrNERmujFa+dt9byi3IupmTJAUCY4ENw3hMRGENE3vP785Pm4w04GYF7JKUMFEYhm6Yd+xcpRu9ZHbiK0bPTmGKaW8CHKAyeTqe2afTQGQDujhjnbGsD7whefwGahIupteOgzsLx8oQ55tjZmJwQ8ng8jUKw5iLvCAwwQSnFe28tckKca9u273vnnLXWIoTAZwJfJc6Ndd6ytmvb5+fnvusA+ezcjVOKLgT8+3BaL130HjmGAv1cyZyLGEV3GjTFnPPU+dypHFxMA3j0/BxciO/egZBqHEfvuY8ZU2hGCwQDWqASYLRRvAwxinH8pzkpSpriAAGd77UbMaBLOUWw/KEpy8U0ZiJYrVZzTubE/SeICBB9OgfOH8j5HX7HkgkWi8Xddrteb27/+rzf7+u63u12u8tkGuu6quoJVVWt1+svf/6xu99U95t6+2VXPdTVQ/2wrav7ajKrqqp6uH98rO+228VicfV/418ST5bYWSEnTAAAAABJRU5ErkJggg==" nextheight="1004" nextwidth="1600" class="image-node embed"><figcaption htmlattributes="[object Object]" class=""><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://meyer9.github.io/base-benchmark-results/machine-type-comparison/#/run-comparison/latest?filters=eyJwYXJhbXMiOnsicm9sZSI6InZhbGlkYXRvciJ9LCJieU1ldHJpYyI6Imluc3RhbmNlIn0%3D"><u>Link to results</u></a></figcaption></figure><p>The metrics page shown above allows for comparing the block-by-block performance of each testing setup. This allows node providers to test out performance on various node sizes before switching, and also allows us to ensure that gas limit increases are safe across a range of clients and configurations. This page also includes metrics breaking down how long each phase of block processing took.</p><h3 id="h-use-case-3-tuning-node-parameters-like-cache-size" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Use Case 3: Tuning node parameters like cache size</strong></h3><p>Finally, we can use this tool to test the performance impact of code and parameter changes. For example, we can test Reth with the SAFE_NO_SYNC flag on MDBX which removes the need for committing to disk in exchange for possibly losing some transactions if the program crashes.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/5ae01403d6d76f748a97ec01b7dfb99221747c2dfbbcf81983b29ca44dbcbc70.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAUCAIAAABj86gYAAAACXBIWXMAAAsTAAALEwEAmpwYAAADyElEQVR4nLVUS24jNxD1Tb31OjqADpCsA/gsWQwwAZJsPEhkObIlWRJsSd3N/pEs/qpINgds2Y6VzEyyycMDQVZX85H14cX19fXst483v9/sdrvD4dDVdcf58vFxsVgslsvF17H80tf5fD6bzebz+d3d3Ww2+/Dhw8V30x9U9+wHrbWtGROcG60ZY1VZnsiqqiozTvOyKPKyyKgZq7Nrxaqqqeu+6wCktVZKSUQAMJ1OL378fsL61EECydumqWvW9z0hEiEi0ivfz18szkkphRCCZwghpJRKKedcCCGlhIiTySQLHBpqIWVHIXsuJKgQ07/ShwjKgNKgtNJGQqaQWhsbYkopGWOzwGQyFQKUNgKMMk5bIh8deqSAFBwFh/5rRBqQAvlIPqIf5yF1Qj8dWN0BF+YkMBFguAQhQRvX9Zx8cEjGorWI6PPPo9hIP/JtGbKz88Y4h55GB2NIgkEaBLwKKG1SSjHmi8UYnXP7/X6z2ex2u7KqEDHGOAxDGobgA3k/vMJ7b60tiuLh/r5mDBFP0T9B6VcBUHoYBq4x5H1iOEccdz8hxkEpbUw+UEopW7JyIh8MkrLOERrEDtSZgNLGWrcsRIj5mOkfGM4FFEgitA6DJ+RFu/1zez+rVp+Wf/yy/PTz4ubj7NefrKiN81dXV1lAa4Pk50cev7D5GWIctDG8a0F0nHMDXPdM1IeO7XlXt03F24L3DW+LIQb9WkX5BkS0fi6cFka2WnEDvdHKQY+y5rzvu1YKTkYOcQBQAJC+ibFKz0KkiVBr5axx1lhrjNEAYEA6o6wx2WAMoQu5BFApNXaeQyTn0FqjlXLWonOjHZ21o4D+K8kpJ4q8D0ShE0oKWTMGbSWEBAneB+9zvsl78jntflwLgxJU3/dtdQDRSQnG2hACEqWUQJ0LvLUM+Ry+puvbrrf5lG/2mN8IoiyTx2CdZ6xumqbr+iajzU8MecRzATUKJIRE6kTdV4ftw3G3tKJ+MyZvQog+vwMvxYDOHouyrFhRVvtjVbLaj33gxyS8hWjKBQAAOZuGmGIYMsddxq4YYkivDCE6pK4uyudtyZgVjWme+uOaHZ74YcWfHw67FXvelLt7b7W2bjKW6bTnom1bJBpSimPvZJ4wDO/pvdfa1FVZ16wF50Pwmst631X7wAtV7zt25M0RmiM6q7S7uppcTKcT51xK6X2XfwPee0T8L57OuRyiy8vL+d3i5ubm4eFhu92uR2w2m7+Nb7i9vZ3NZpsRL6bVer1ajcvVZr1Zr1br9frx8XFxf395eXnxf+MzLMaNvb0yekoAAAAASUVORK5CYII=" nextheight="1004" nextwidth="1600" class="image-node embed"><figcaption htmlattributes="[object Object]" class=""><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://meyer9.github.io/base-benchmark-results/compare-sync/#/run-comparison/latest?filters=eyJwYXJhbXMiOnsicm9sZSI6InZhbGlkYXRvciJ9LCJieU1ldHJpYyI6Ik5vZGVBcmdzIn0%3D"><u>Link to results</u></a></figcaption></figure><p>The results of this test showed a negligible difference between the DURABLE and SAFE_NO_SYNC options in Reth. This means there may not be a huge benefit to turning on the SAFE_NO_SYNC option in Reth.</p><p>This run shows how the benchmark tool can be helpful for testing out new client features. Reth recently added parallel state root calculation which could be interesting to compare against single-threaded calculation. Base Benchmark should make it easier for client developers to push performance forward.<br><br>All of the configuration and results for these tests are available at <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/meyer9/base-benchmark-results/tree/main/configs"><u>this link</u></a>.</p><h2 id="h-how-ethereum-can-scale-faster-by-identifying-dos-risks" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>How Ethereum can scale faster by identifying DoS risks</strong></h2><p>In the long run, scaling is all about doing more with less. By reducing risks like DoS vulnerabilities, updates like certain EIPs can unlock higher scaling ceilings, allowing the network to grow faster and serve more users.<br><br>For example, in the Dencun hardfork, one change reduced the possibility of slow database operations triggered by the SELFDESTRUCT opcode. By reducing the performance burden of the opcode, Ethereum removed a security risk that was worsened by scaling. The new Base Benchmark tool aims to surface bottlenecks like this so we can get ahead of addressing them to continue scaling Base safely.</p><h2 id="h-help-make-ethereum-faster" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Help make Ethereum Faster</strong></h2><p>With Base Benchmark, our hope is to have a standardized set of benchmarks that we can run for any client to evaluate scaling limits. <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/benchmark"><u>You can download and run Base Benchmark immediately by following the instructions in the repo.</u></a></p><p>For node providers running Base, we suggest following our <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.base.org/base-chain/node-operators/performance-tuning"><u>recommended hardware specs</u></a> and the configuration available in the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/node"><u>base/node</u></a> repo. We encourage you to play around with different hardware specs and configurations using the Base Benchmark tool to measure performance. Our hope is that we can collaborate to improve node configurations so everyone can benefit from performance tuning improvements.</p><p>If you are a client developer, we highly recommend <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/benchmark/tree/main/runner/clients"><u>implementing the client in Base Benchmark</u></a> to include it in our testing suite. This will help compare performance of different transaction types against other clients to find performance regressions or optimization opportunities. Once implemented, the client will show up on our <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://base.github.io/benchmark/#/latest"><u>public test page</u></a>.</p><p>Anybody else is welcome to contribute to transaction payloads that can be tested, supported nodes and configurations, and metrics to collect. Although we currently support Geth and Reth out of the box, we’d love to expand support to other clients like Nethermind and Erigon.</p><p>As part of building in the open, we intend to make our benchmarking results public and share them out as part of our scaling journey and process. Right now, our <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://base.github.io/benchmark/#/latest"><u>public test page</u></a> shows results from a devnet run with no existing state. We’ll soon expand this to include results run on Base Mainnet state.&nbsp;</p><hr><p>If you’re interested in helping us build a global economy that increases innovation, creativity, and freedom, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.base.org/jobs"><u>we’re hiring</u></a>.</p><p>Follow us on social to stay up to date with the latest: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/base"><u>X</u></a> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://x.com/i/lists/1876354838289932418"><u>Base team on X</u></a>) | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://base.app/profile/base?tab=posts"><u>Base App</u></a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://discord.com/invite/buildonbase?utm_source=dotorg&amp;utm_medium=nav"><u>Discord</u></a></p>]]></content:encoded>
            <author>base-engineering-blog@newsletter.paragraph.com (Julian Meyer)</author>
            <category>benchmarking</category>
            <category>scaling</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/653238d749355cd7dbd1f0a8c644c06c890763211e280f10da340e06d7babdd4.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Scaling Base: Doubling capacity in 30 days]]></title>
            <link>https://blog.base.dev/scaling-base-doubling-capacity-in-30-days</link>
            <guid>LskMlFFUFreHIdSUQlkL</guid>
            <pubDate>Tue, 28 Oct 2025 15:15:27 GMT</pubDate>
            <description><![CDATA[TL;DR: We’re continuing to scale Base to serve as a platform for the global economy. By the end of 2025, our goals are to double Base Chain’s gas limit, improve client performance, and expand L1 data availability to meet growing demand while maintaining sub-cent transaction fees.]]></description>
            <content:encoded><![CDATA[<p><strong>TL;DR:</strong> We’re continuing to scale Base to serve as a platform for the global economy. By the end of 2025, our goals are to double Base Chain’s gas limit, improve client performance, and expand L1 data availability to meet growing demand while maintaining sub-cent transaction fees.</p><p><em>Special thanks to Trent Van Epps (Protocol Guild, Ethereum Foundation), Alex Stokes (Ethereum Foundation), Eli Haims (OP Labs) for reviewing drafts of this blog.</em></p><hr><p>Base’s mission is to build a global economy. To achieve this, we’re committed to continuously scaling Base Chain so it can serve as a platform for billions of people to come onchain. We started 2025 at 10x the scale from when we launched Base Chain a year before, and have since made significant progress towards unlocking more capacity to meet growing demand while maintaining sub-cent transaction fees.</p><p>At the beginning of this year, we identified multiple <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.base.dev/scaling-base-in-2025"><u>scaling bottlenecks</u></a> that we’ve been proactively addressing through large scaling projects that are now nearing completion, such as migrating our infrastructure to Reth, optimizing performance with TrieDB, and expanding L1 data availability with PeerDAS.</p><p>These projects unlock stepwise performance gains that now enable us to scale Base Chain much more quickly. <strong>As a result, in Q4, our goal is to double Base Chain’s gas limit from 75 to 150 Mgas/s.</strong></p><h2 id="h-our-progress-thus-far" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Our progress thus far</h2><p>Through the first half of 2025, we performed scaling increases on Base Chain nearly every week. As a result, we saw median fees drop from ~30 cents per transaction to fractions of a cent. In mid-Q2, we paused these increases to ensure chain stability and focus on the longer-term scaling efforts that promised to unlock larger scaling gains.</p><p>In the past several months, Base has continued to grow: more breakout apps have emerged, bringing 10x more onchain activity to the network. In June 2025, Base Chain successfully handled periods of up to <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.base.dev/scaling-base-sustain-10x-growth"><u>~1,500 transactions per second</u></a> with median fees of under 5 cents.</p><p>During this time, we <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.base.dev/scaling-base-sustain-10x-growth"><u>reflected</u></a> on how proactively increasing Base Chain’s gas target ahead of organic traffic can lead to a suboptimal fee market. Therefore, we’re now continuing to scale Base Chain by increasing its <strong>gas limit</strong> (i.e. maximum capacity) instead of the gas target, since gas limit increases have less impact on the fee market. We reserve gas target increases for when we observe high fees.</p><p>As we charge ahead with continuing to scale Base Chain’s gas limit, we provide an update on the progress we’ve made on tackling scaling bottlenecks.</p><h2 id="h-scaling-bottleneck-review" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Scaling Bottleneck Review</h2><p>We have made significant progress towards removing scaling bottlenecks since <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.base.dev/scaling-base-reaching-50-mgas-in-q2"><u>our last update</u></a>. As a reminder, we track 4 main scaling bottlenecks:&nbsp;</p><ul><li><p><strong>L1 DA</strong>: How much data we can post to Ethereum</p></li><li><p><strong>Client Execution Speed</strong>: How quickly blocks can be built and validated</p></li><li><p><strong>Fault Proof Performance</strong>: How quickly fault proof games can be played out&nbsp;</p></li><li><p><strong>State and History Growth</strong>: How much disk usage nodes are required to maintain</p></li></ul><h4 id="h-bottleneck-1-l1-data-availability" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Bottleneck 1: L1 Data Availability</h4><p>On May 7, the Pectra hardfork went live on Ethereum mainnet, doubling the available blob space. For more than 6 months leading up to this, blobs usage was at capacity; after the hardfork, L2s were able to utilize more space and accordingly settle more onchain activity.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/61fb3af55ccf132d18e4674abf6ba90dd01a424f10117f0424abf087d0e1f151.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAASCAIAAAC1qksFAAAACXBIWXMAAAsTAAALEwEAmpwYAAADCklEQVR4nK2VTUgbQRiG56LQ1hQUSQ0N5JB26R5C3TroQrxVcvQu1ENa8CBFmoMnSSliCoq/KaSHoIFGrUWLpwZEI2lJJEQIrbSkPfRkbLPNf0xmTSbJlOzEdJtqqdWXj+Gdndnvmfl2hgWvlhYfDQ09HBwcG30yNzMzNjr6eGTE++4tIeXcYSYvovNEOhYFmxsbs3PWyalpt3sbIZSWhDEmZxfGxbon6DADKpw/kuJ/0EHsR13EspkkytYD0GHmPxZLCNmPCqVyqVQuYYxL5RIhpLe3d3Nr6yIB+FiEkHg8fqWpqbu7W16JcwIiRanogiAQQsxms1Kp7OvrkwNyFJAvFBBCGBf/GlTVLkJi8ghZLBaHw2E0Gg0Gg0KhYBjGaDSesINjQFWoIlHWFaUql3GpikEIEUKWX68BABoaG5WSWJbV6XT9/f2VvNJpdLu37bbn9SWq8el3o8qLKBTYScUE+Zx7D+4zDKPX6yGEekksy1JAOHywvr4+MTk182wWZFMJQsjiyssXaysJMff1275leuLN9mY6fxRJJhJizvx0TN3SdAmABgC0N29cvqpoabsGAGhpbdXr9RzHQQhpSwEU73a77fPz7z99qAKsVqvNZiOEmEwmAIBKpeq52+OWBAC43tYGJDEMo5akUqkYhuF5nqaGEPI8X9sBrRJCKJtKVQFms3l8fJwQQhfF87xGo1Gr1c3NzVqttrOrqwN20iy1mtDuaYBfp6gGgBAaDAaNRkNf4yVxkpHXoWbkQ6cBKqcom0oRQoaHh5VKpVarhb+Lk6WmLc/ztaEzAAYGBliWpRu/YACSjqnJZGIYBkJ4h6sKwg4IO3Q6HTUyX0nNcVx7++2al2BdLHur7iZXAAlB8Pl27HZ7ILD7PRLZDew6nc5AICAIgtfrnV9Y8Pv9giAEg0GHw+Hz+ah3Op0ejyccPgiFQkvLyx6PRxCEvb291dW1UChE70o0nYzHo8Djcm24XP4d3xFCRYwLolgQxSIu0Dj2WOYr5qQ5lWkiQvQ3lYzFfEH/5y8ffwLWI7OJbfvswQAAAABJRU5ErkJggg==" nextheight="815" nextwidth="1429" class="image-node embed"><figcaption htmlattributes="[object Object]" class=""><em>Source: </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://dune.com/hildobby/blobs"><em><u>https://dune.com/hildobby/blobs</u></em></a></figcaption></figure><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-7594"><u>PeerDAS</u></a>, projected to go live in early December as part of the Fusaka hardfork, represents the next significant advancement for L1 DA. It addresses the challenge of increasing blobs without burdening L1 consensus nodes, by sharding blobs across various nodes.</p><p>The implementation of PeerDAS was a huge collaborative effort, with numerous ecosystem teams advocating for its accelerated release (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.base.dev/achieving-more-blob-space-in-2025"><u>1</u></a>, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.optimism.io/blog/peerdas-and-a-48-blob-target-in-2025"><u>2</u></a>, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.sigmaprime.io/shipping-peerdas.html"><u>3</u></a>), effectively reducing the timeline by several months. Fusaka lays the crucial technical groundwork of PeerDAS, without increasing blobs. Once the implementation is observed to be stable, blobs will increase in Blob Parameter Only (BPO) forks, anticipated to land very shortly after.</p><br><table style="min-width: 100px"><colgroup><col><col><col><col></colgroup><tbody><tr><td colspan="1" rowspan="1"><p><strong>Projected Date</strong></p></td><td colspan="1" rowspan="1"><p><strong>Fork Name</strong></p></td><td colspan="1" rowspan="1"><p><strong>New Blob Target</strong></p></td><td colspan="1" rowspan="1"><p><strong>New Blob Limit</strong></p></td></tr><tr><td colspan="1" rowspan="1"><p>12/3/2025</p></td><td colspan="1" rowspan="1"><p>Fusaka</p></td><td colspan="1" rowspan="1"><p>6 (no change)</p></td><td colspan="1" rowspan="1"><p>9 (no change)</p></td></tr><tr><td colspan="1" rowspan="1"><p>12/9/2025</p></td><td colspan="1" rowspan="1"><p>BPO 1</p></td><td colspan="1" rowspan="1"><p>10</p></td><td colspan="1" rowspan="1"><p>15</p></td></tr><tr><td colspan="1" rowspan="1"><p>1/7/2026</p></td><td colspan="1" rowspan="1"><p>BPO 2</p></td><td colspan="1" rowspan="1"><p>14</p></td><td colspan="1" rowspan="1"><p>21</p></td></tr></tbody></table><br><p>As a result, we’re on track to more than 2x blobs as an ecosystem by early 2026, which we hope will be sufficient to support Base Chain and other L2’s continued scaling plans in the near future.&nbsp;</p><br><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/8ee6e05f20d578c620b8e92fac08cc590a0055a3509aa1b1dde5ec7b58a52598.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAYCAIAAAAUMWhjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAD0UlEQVR4nK2VT2jbVhzH5bMvg9FBGLjborK4sB4GYgd1B/XikwOlhg3EDjqMajB8U2Eg96Bdpo4lVrb5wdjmw4IuQe1aNFYwpVu9EDAkUGXBaE7mt/yzktiOtBTJhc2/Ib9MtT3HbUa/PMT7Pb3f+7zf70nvR8HTpOs6y7KJRCKdno7FYqIo6rqezWYZhhEEgenJcZyT3CkA0DQtkUjIsqz9RwghnuepQbEsS9M0RVHxeJyMSJKEEIq88vkZ9ZOPb9z41DTNEIAxLpVKmqYFQeCOEgAEPZFNRX3SGfLyvKPGbn115UfPO8rnZ0NAuVxWFEVV1ZPCDPoWCkZpaL7vt+vrvwDA53NzIcAwDJIfeB76u9v1/TZeXwQAhAohwHVd27bHA3y/7R02+p/eoTPSDHxvb7e69OBmwzlQVZVyHEeSJJqmFUUh2RhcuQsA7eYGXl9st+rt5oaz82u7Vd/brbabG+3mRthp1VsHNWJ6bv3Bz6Vvvpr7/ubC/Pzta9c+Cj8Jkn2E0KitdwGgsf2w3dyIzPG6dbtkrdqkjxCiEolELBZLpVJjImj8C+h2/+qNjGuFwneu6z0BsCyLEHJd94Qz6J4qAtf18vmvIzMEcByXyWSy2SxJ1PgI4GmA5eXVO3fuDQBEURQEQZKkMSna/H15v1F7FsDCwg/37y8NANLpaU3TMMb5/CxAtxM8etwJfP8oCPw/vcNO8AgADvbWdrar+I+dzc3tra3GmIbQkwM4BvA8r2maIAgfXH2vuV+tVRe36g8P9tY8t+77eGX5Hvpidurc65OTzJmJN18++9aY9sKZC++8+2F/QCFgYmKCJOfLQiF6sbS0cv36zOS5t1+lL06dvzST/3Zt7Tc4vUJAqVQyDIOm6VwuNz9/6/Ll91986cJr9MUrV67evfvT/n4rmt3pPH6WNgwgvSDwz77yxtT5S7ncZ7UajuZ1TvA8RQQA4DiOLMu5XC56Qe7I/7foCADG+DnepqNTRBgYY9u2McaWZdl9siwrehU9hwaJ2e9oWVZ4mxKAqqosy5qmSX7pYrEoy7JhGKIoFnuSZZmUz2KxmMlkUE+CIJimKQiCruuyLCuKouu6JEmmaSqKomlapVKhyK9bLBYZhglrNEVZlkWqeRAEFBXuIB6PS5Kk6zrP8wDAcRwApNPTHMdZlkXm0DRdLpdFUeQ4znVdigoLwXHRJ6dKKirGGAAsy3Icx3Vdx3GCIDBNEwBs204mkwzD8DyfTCZTqRTxInOIY6VScXqqVCpk2WNA/y0UFfSB8+oVPkmSSK4URTEMY8hxpPkPP9VUZRBsGSQAAAAASUVORK5CYII=" nextheight="476" nextwidth="635" class="image-node embed"><figcaption htmlattributes="[object Object]" class=""><em>Pectra and Fusaka have both provided significant benefits to addressing this bottleneck.</em></figcaption></figure><h4 id="h-bottleneck-2-client-execution-speed" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Bottleneck 2: Client Execution Speed</h4><p>This is our biggest bottleneck today and the focus of several of our active projects.&nbsp;</p><p>To scale safely, we need to ensure that both sequencer and validator nodes are able to build blocks fast enough. These blocks continue to become larger as we scale, and we ideally can build confidence in nodes being able to build these larger blocks <em>before</em> we scale. To solve this, we built a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://base.github.io/benchmark/#/latest"><u>benchmarking tool</u></a> to simulate block building times at a gas limit we specify, with various traffic patterns. We will share more details on this tool in the coming weeks, but this groundwork illuminated specific performance constraints we needed to address prior to scaling.</p><p><strong>Effort 1: Reth Everywhere</strong></p><p>We are actively migrating Base Chain nodes from the original default client, op-geth, to our new Base client, based on op-reth, which we have measured to be significantly more performant. We have <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.base.dev/scaling-base-with-reth"><u>made notable progress</u></a> by migrating almost all internal node infrastructure, most recently our sequencer nodes, to Reth.&nbsp;&nbsp;</p><p>The only nodes we still run on op-geth are parts of the fault proof system, however we will be moving to Kona, a Reth-based fault proof program built by OP Labs, after an upcoming Base Chain upgrade, U18, enables this.</p><p>Finally, to fully realize the performance improvements of Reth, we need to enable Reth to fetch historical state efficiently, which it currently is not designed to do. We designed an <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/op-rs/op-reth/issues/162"><u>ExEx</u></a> to improve efficiency, which we are collaborating with OP Labs to finish implementing and rolling out.&nbsp;</p><p>For external validators, we recommend Reth as the default client moving forward.</p><p><strong>Effort 2: TrieDB</strong></p><p>Over a year ago, when our team benchmarked Reth, we noticed that, while Reth was far more performant than Geth, storage reads and writes were the long tail of execution speed. Speeding these up could provide an 8-10x performance improvement.</p><p>As a result, we invested in TrieDB: a project which restructured the database format to make fetching state much faster. We’ve been actively developing this project for several months and are close to having a final version. We have <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://youtu.be/LOxkZcASH0s?t=3048"><u>spoken about</u></a> and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/triedb"><u>open-sourced</u></a> this project and will be sharing a more detailed technical explainer and timelines soon.</p><p><strong>Effort 3: Resource metering and opcode repricing</strong></p><p>While TrieDB focuses primarily on improving the speed of storage reads and writes, there are other opcodes that are also mispriced today, compared to the resource load they put on the network. The ideal solution would be to reprice these opcodes across the board. This sounds trivial, but in practice is a massive coordination and execution effort, so we are working on this in parallel to a more immediate solution.</p><p>One solution is managing resource usage, or “resource metering”, to ensure that Base Chain nodes remain stable while ensuring that user transactions are quickly confirmed. We already use this today <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.optimism.io/chain-operators/guides/configuration/batcher#batcher-sequencer-throttling"><u>to manage Base Chain's blob usage</u></a>, the most critical resource that rollups rely on to inherit security guarantees from L1. Optimism is formalizing this in the next OP Stack upgrade by <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/ethereum-optimism/design-docs/blob/main/protocol/da-footprint-block-limit.md"><u>adding </u><em><u>in-protocol</u></em><u> metering</u></a>. This allows the congestion management from Ethereum’s EIP-1559 to kick in so users are less likely to experience delays in transaction inclusion.</p><p>Separately, we have been working on a new project to revamp the mempool on Base Chain, which enables features like bundle support and transaction status tracking for users. One big scaling benefit of this project is that it allows us to more easily apply resource metering to other resources besides blob usage. There are several open questions and decisions around the design of this, which we will be sharing and soliciting feedback shortly.&nbsp;</p><p><strong>Effort 4: Access lists</strong></p><p>We have begun exploring <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/op-rbuilder/blob/de025169183ef7d4bd9cb77ed96515943641e86b/docs/FAL-Flashblock-Access-Lists.md"><u>Flashblock Access Lists</u></a> (FAL), an adaptation of <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-7928"><u>EIP-7928</u></a> for OP Stack chains that produce Flashblocks, along with OP Labs. FAL improves execution performance of validator nodes by enabling parallel disk reads, parallel transaction validation, and executionless state updates.</p><p>Summarizing all of these efforts around execution, the Reth sequencer migration has made it safe to increase gas limit to at least 150 Mgas/s, and we believe we can unlock a gas limit of 400-500 Mgas/s by early 2026, with efforts like TrieDB and resource metering landing soon.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/ded2dd4a116ad4e9c7813bba749a327d8d4f67f343c8e3b54e754249b63226fe.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAYCAIAAAAUMWhjAAAACXBIWXMAAAsTAAALEwEAmpwYAAADr0lEQVR4nK2VX2gcRRzH9/VeJXAK97IIffDwz7ZgQAJyWD0K7otLhUVh28KCchBh8M+8eBYdrXRfZBC6MTWL9Y6zydCYpW26D8JaI9vmz+LRbq/gkmo3RbKaK1OjTBLsyGXO7XmNpTb5sizzm53f7zO/mdnfSPw/lKYpAEDTNISQqqqVSsX3fdM0AQCVSkXTNMMwqtUqhBAhZFkWhBAAcHcciXPuum6xWKzX616PCCHSv6XruqIokiTl8/m+T7Isi4bv+57nnTl9ZmpyyvO8IAg6gCRJCCEQwjAMgx4lm4rjOEkSSmkURaIdx3GapuKraIj+JEmE47mz50bwyNz8HEKoC8AY27bNd0g3V26GQcg5H/1stAMIw1AsKOecMbad0IyxjfWNdtoWANu2OwDGWJqmjuPsYAbnp7/tAiiljuPkcjkI4fYzcBvu2VPe4bc//uC9T6amvhkeflMyTVOE3pE9aIXR9PT52dmmMDsZ6Lqez+dLpdKOZDD/3Wyt9vXiYnIHUC6XAQCUUozx9jO44F8cG5vIzA4AAAAhxBgf+egI53z199WN9Y0Hezjnjc8n+gGiEoiScI8l+uv27XvPXQA+PToyMzPXn4H4Az58H10Jr0QLly8vNC/NX2o1WwvfL7SarWtX4+WlX1aWfx3/cvLE6Mna8fHa8fGvvjh1YrSx+T6ZmZON0/tfPJRtQBcAIbQsS9f1gwcOttP24o/XksWlpZ+X1tfW/lz9Y+W39njDPXTgrUJhcGDg8UJh8JGHdxcKg1kjMwuFpx8aePKF51/tTasD0DTNNM3smDK2xjlfTlfGxib2v1zZ9djewWdeegcevXDxh1u3Vu9nn0WEO4B6ve66brlchhA2m1ffGD781O59j+56du9zrxw7VrtxY7nH877Un4FgXr/+0549pSeUfa+9/u7MzFzvOMbWeif1v9QBCCzGuK/Y3T2dB1A3A0pptVq1LGv7f3KfHMeRsgsSIRTHsbhVwjCMehT+Y2YDRGevuaWjZVldgG3bqqp6nmdZFsbY933LsoRJCHFd17ZtQggAwHVdAADGmBBSrVY9z0MIEUJs2xaOGGPP8xzHqdfrvu93AUEQFItFzrksy2K0LMuc81wuxxgbGhoyDCMIAlVVOeeqqopVVRSFUipJnZpfKpWEo6IowjEMO3dOFyDuHM55FEWCF8dxZhJC6KZEUYEQmqap63qappRSEShzFA3f90XYLqDv/Gy524wxCKFhGAghwzBs297yRPR1/g3OUU4nlGlTpAAAAABJRU5ErkJggg==" nextheight="479" nextwidth="635" class="image-node embed"><figcaption htmlattributes="[object Object]" class=""><em>Our recent migration of the Base sequencer to Reth has enabled a stepwise performance gain, and as more projects land later this year, we will unlock even more ability to scale.</em></figcaption></figure><h4 id="h-bottleneck-3-fault-proof-performance" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Bottleneck 3: Fault Proof Performance</h4><p>In our last update, a major bottleneck was fault proof performance. Given that Base Chain still relies on an optimistic proof system, we cannot produce more transaction data than can be properly proven and verified by our proof system. We are bound by how long it takes for fault proof game participants to produce successful challenges.&nbsp;</p><p>However, OP Labs produced an improved version of the fault proof system which introduced multi threading and utilized a 64 bit architecture (over 32 bits) - MT+64 Cannon. These two modifications significantly improved performance, and fault proofs have since not been a concern for scaling post rollout.&nbsp;</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f4df4ca9983ad00882104fc3dbfd2fbc4bac9a0dcb0bcc61715121067ea92437.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAYCAIAAAAUMWhjAAAACXBIWXMAAAsTAAALEwEAmpwYAAADi0lEQVR4nK1VTWwbRRRen3NBsnpAqAkVy63FAslQMUHCFyKarsVhb6tKrNrCioMBwaSXjdruBQ0Sh70w9JDsoWEPqfZQNPy4VnDktHWzUepmqEpWFmhFD+ypGqD1OFLRoO1Yi2Ns4yA+jVZvft775s23M08RI8A5F0IEQaBp5UKhcPads/l83rKsIAgAAJpWLhaLAABd1w3DKBaLjLGhcRQhhO/7qqoihHAfXNe9vHLZMAxlPzSt3N/N5/PSsG3b87yBCISQlCCKoiAIEEJy4xkedTpCiBbd+b6xnuXU/x3IeABCCNd1ewQIIdd1h+bYojvNrU0hBGMsiy4hR7Jw/wTGOCXY2NgwTdO27aFb22xtrTevD50ahW53r9vd+zsDznmSJBjj8Rk8/vPxhAR9GXyuMMYQQrlcDkI4dJutAxK02/Hi4mdLy6tXrnxz5sx7iq7rUt5RGtymtxu3bkx+RNVqo15vyoNKjwgAMDU1BQAYpcHd3R9bdGfyDJaWV9vtWNopQaFQgBDKgxrqsHkQkbvdvaXl1aybEpimaVmWbdujCFo/0HB7a8IM2u340hdfSqYegWVZmlbWdT0TeeCyNLc2+y/aeNTrzWq1sY/ANM3xIrfojhS53Y7Ht/v3f0UIZwLsIzjx5on58slqfS346iq59t3Xa9/euXcnpGHj1o13P/jw6EuvHz32xqGnX3xm5pUx7alDL8zPv92/uZ7IlmUJIS5cvPjg9wd/dNhD/nBtfWPx/Kcz6suKclhRDp8+/XHt2vXJb/I+At/3CSGzs7MQfrS9fffcuU9mjrz63POvHT/+1srK1Z9/+mXgAfjXNkgg1dvdvTfz7LEj6uypU+/fvLnN2G/ZokedTqfDBzwPkIEkwBgvLCz0z3GeBu3+p7iDBEmSQAgdxxH/N7B8riWHbdtRFFFKoygKw5A+QfRkJAxDacRxnA1mRvaVjtkspdRxnB6B67qlUokQ4jgOQkgaQRDIQuj7PkLI8zzLsnzfr1QqCCHf9yGEQRBACD3PQwg5jlOr1aSjrJq9kimEIISoqiqEyOVyMvr09HRaspV0gaqqhmHUarW5uTkhBACAcw4hVFWVMaYo6ZsPAPB9H2OcOYZh2Cv6shzGccwYo5RKPkop51x2McZyQalU0nW9UqlYlqXrehzHSZLINTIcISQMQ8YYIUT+KT2CScA5N01T13XbtjWtPOFP8ReLwEcdMmcIVgAAAABJRU5ErkJggg==" nextheight="477" nextwidth="634" class="image-node embed"><figcaption htmlattributes="[object Object]" class=""><em>Since the upgrade to MT+64 Cannon, fault proofs are not an immediate bottleneck.</em></figcaption></figure><p>In addition, to continue decentralizing, we are exploring ZK-based proof systems for Base Chain and we believe these could have positive benefits for scaling.</p><h4 id="h-bottleneck-4-state-and-history-growth" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Bottleneck 4: State and History Growth</h4><p>Our “Reth everywhere” efforts have paid strong dividends in managing state and history growth as well. As a result, we do not currently have major concerns for historical growth becoming a scaling blocker in the near term, however this could change as we begin to significantly increase the gas limit. We anticipate that one of the next long term projects we take on to address this challenge will be around state expiry, and the team is beginning to explore potential avenues in this direction.</p><h4 id="h-bottleneck-summary" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Bottleneck Summary</h4><p>Combining all of these, we get the following summary graph. Notably, we have cleared the path to scaling to 150 Mgas/s in the next month.&nbsp;</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f93e292bf59d1d19b305271ccfcfcadcd47b58c00f5fca522406467e05aaf286.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAYCAIAAAAUMWhjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFJElEQVR4nI2WX2zTRhzH/TBp6sMQYUihRLDlYWEi7CHADGyZWHjJy5AmeXuIpi1PQaNRN5FWw6DhbponqK6FKTBmRkM6te6kKoPhQSNTmllJi0FAugIOHbhpmVsVXJrW/ZM6kOYm212UNbTw0cny7+y7r3+/3939jMBlUVUVQhgIBOx2u1fHarV6PB6n0+lwOFwul9vtFgRhmRkQCCFN0zabDQAQfhZNoabt7+xAEMRisZjNZkTHYrEgCGIymRAE8fl8kUiEoqjikNOnToEj9U2hEMMwmoAkSTzPUxRlfHKRbDYLIYzzPWysq+hN0bOiqS4mNzoyEutgIIQURWkCoigSBAEAeKaPt1J3rty4Vj6RoiilMqVMjGcSbCeEMBwOawI8zxMEEQwGlxK43nsTQpifz8MXY3pK6enSBM6cCWkCiqIIgmCEqJw+oe/KjWv5/OxEZnQum5mafDiXzSgTI/q9XGpmZ8cfPxx9/PDR3b/62k43KbPZhoajiCzLwWDQarWSJLko0Ab9A+lET8fo8I3M+NDEeDozPjCZeSCPpibG06XmZObB9NTQhbZ2lok2fv9DiGqJJ67X1h5AMAwjCMJIyDM96BVS5y80z0yPzBcKEBaWD87dpBCNxpO9CwtXC5HT6bRYLC6XaxkPOrvap6ek+UKhUMjrGks24ead1tbf02nJGKutIgzDgsGgoihLJTklDnREW1/Qg87znSeONxdNTcDj8Xi93kAgsNQyvX23/4+LzZOZB6qae5LLPX3ydKkGIWxvOdfe3gEhNExNgCAIv9+P47gRonL+Tg91drU/1wNVzUEIwXfHo9F40dQEfD4fAECWZXCkfmRweLBfHE4PpZK3BvvFOzf7Hg2Pclx3yy/H7t2+1nH2ciLW0x3jOTbeHeNZ5rJx3xXlErEeltGe7vl039CglJ/PG7nUBPx+P0VRgUDNzp07GsBBAL4+2ljXAA6cOxtqbWmsP3xw+46tCIJY122teOmNlSs2rnp108oVG1evfuuVl22VazavX7etsnLLa+vfXVuJrl277eOPqkrd0gRQFA0EAhDCxmNHZ3NzM+rMjDpzvqPzYF3D67b31qx/e8OmXSdOtgopsTws/NXkyZ/amkKhb749dLrp58/37jlSf9jj8WA6QR2E00FRtLamlmUTVXuJDW/u2rzlA+9nNdyfV8fGMvpUBVXNlbfmZrqqKlBd/SWCIDabzWRaZTKZCILwer1+vx/oaEcFhHBuLuvY/D667cO6umMDA/8YKSo54HLFntKs9qd676d6fm2jnU6n1WrVNUwURUUiEVmWtZwbAoqiAACM/fzceUsoxLriLJvA8QMVFRXG7A6Hw+v1Op1Onuf/J0CS5FL7YBm6u+PMhd8I4pDZbHa73RaLBcMwSVrYxqqqajkwDEmSAACSJImiKEmSIAiijqT3LDKN673795tP/Fjj+WTfF9VGdcMwzCh2/H+QJLkgQFGU2+1mWdaoPAzDEATBsiyO4+FwmKZpkiRpmjaqo7F1aJreW1V18dKlr/bvZ1mWoigAQDKZBABEIhEAQDAY5DhuQYBlWRRFIYRms5njOIIg7Ha7VrIR7QW73e73+zmO83g8EMLdu3erqorjOIqij8fGEARRFMXlcjEMAwBwOBwQQpPJZPwMLAioqirLsqqqRgSTyaQkSUYnhJBhGOORS8fn87lcLgzDFB1jiChqGyWZTIqiqCiKkWRVVRcEyg9qw1xU6EmSxHGcoiiSJCORSPk75ea/K+3mYouNenQAAAAASUVORK5CYII=" nextheight="476" nextwidth="634" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><h2 id="h-guardrails" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Guardrails</h2><p>We believe proactively increasing Base Chain’s gas limit is core to supporting a billion people onchain, so the core metric we use to track scaling progress is gas limit per second.</p><p>However, the intended impact of scaling is to maintain sub-cent transactions on Base Chain so we must also track fees to ensure we’re sustaining this.&nbsp;</p><p>Since transactions can range in complexity and therefore cost, the fee metric we track is the cost of a standard amount of gas usage. We have started with 100k gas – a bit more than what’s used by an average swap.&nbsp;</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a61464425e64effacffbc90e239f52ae30340c1535be4a1259c23aa1433fb85f.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAeCAIAAADCaIt+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAGm0lEQVR4nJ1WfWwTZRi/qIASNfMPJOoQYRuMIB9x2cI+lI84tVJYnfPmoO06S4XuOsbKBmuFtYVu0Apbw8aKomvngrdFtwLi1ZmsDcS10cClmHHDKAfTrS3CrptZdwXveE17XfeFc/H55573637v83t+7/O+EI57dskVWl1NnamhztQg3SEvVuypPmJE0TaVWlNaViH5QGa3d4L/a1Dfrb4mS3Mrembnh3KhSCKT7ZQXK96DC8582Xr+bIdAkLc1J1er02OYHUXbvF7vHyTZ1tqGoq0YZreF7ZzP5++5RqBoK4q2ulwum82GYRhFUVEA7nPv3mBt7ccGgxFBShCkBIYLTjaa+29d31dRoVYfQJCSHEEuj8e3WKyX7J2SwiKRSALDBZLCIv6Wre3tHe3tNhguEAoL5fLi/PztPN5mh8MxCUCtPpCVtR5BSmKh0XSIpkMMy9I0PbGTYdkpJHAzp3dOAiCIXofDieN4IDBERyzihGiapiiK6xlzQhPncNMCgaHYNG40EBiaBIBhdoPBaLW2cNuJrBkec4amOdEhmg75fH6fzz99dCqA2XwKhgt0usOzBwhEfoEoykRiKQDA778zE0CENc6i3E1w6GlOKNYUiaVvvMUHAMT+OHE0DMC1lcq9c+fO5/E2x5BiRFNTcxAd4oS4TSjZtOktAECEqH/PwfTszzKC7cIiDmCGCMKeWn0gLS1dJtvJKWc2EdBjFMGwEADwHyoiSRLHPSR5c/YRAAD6B7wqtUar0xNE7xQOxiPwer0GgxHD7GbzKRRtfRhX9L8BkOTNCEBNzzVi4kKOhigAhtkhCOro6BiTKT1LigB4QBC9laqDVdrDl6/gUxaOU4TjHgiCZi/TQOT0zhDBbzdIGBaOlwqCIBISkhSK3XFxC3i8sKJniAAAkLR8tdN5secacXdw0Om8WGdqaLI0W60tXJ4BAO3tNm7HUQC3252cvHIGmQYmbLmry/ncC0t7rhFwvki6Q159xNjcZOn6rrNKq4+FWGdqSEnNuHmzb1IOdLpDCQlJMFwwXaY+nz9p+WqKoi5fwUViqc12DlGUqdSaY8dMEllxEY+fsSTJYGqIASCKsiqtvtF8muuBAoEht9tNURRB9JIkOV0qTufFZclrmizNRdKdvb2/3PbfTs/coNXVAAAYlpVtyclesUqtq+671ed2/2Q0Hlcq9zfUN9aNQUbJstlsSmWFxWLt6nKKxFKtrqbb9SOHUX3E2Gg+nbR89dmzF7jJKakZiKKMKw/iN7Ol/LfLyyu3C4uKFXvaz31z/erPCyGI2wFNh6IAFosVQUo02kOIoszn81/A7NuFRb/dIFNSMzZsfJOiKBz3+Hz++wzzdVPTS4uT1i5a2lhdfZ9hls+Hlj0OWT5vSc/cwMV0yd75AgQVFu6YdJIj9oCiKJ/PzzVUao1Suf+o4XiTpZlbOTw0HFbRnMdrq3Sa4t2V0h0ffahIWQQlPwl1tn9zn2FommZY9osT9bItOSePGsM3cWgsAgQpiY9fLBDkMSw7EgwCAE5/boWgJ0aCQYZl/f39l777nqKo3qtXS9/f9m76q5VS6d/M36JsnpifKOYnynPz7jPMlW43w7JV8mJzzdF6nZ5h2XGAmB1XH9Tv2WvYpy4rlKrUmtZPPvP8+JNaJleKhIZ9qtL3t7m7ut5JW7/sMcjwUX6tRnYOrTmPHs1MWLhfsuuddeta6hvLxZKR4eHaA5pJFCmVFWlp6Xm5eeWK0m25fP7GtAUQtGTOU49C0KMQlJL4fHZmwrqVS1585GnRu5ufgaDMtc/q9Sr+60mHdAf1etUTUNjWLI1bHb80a82qlxfPq1SWZmS+ZjAYowBer5cgem/c+PXEYZnpUOFXVk2pOGMJBDXUFMnzXznzaeXA755TxxCFcO3h8i3fflU/OPgnTYcGqbAxLIt+pjlYku3APq3azavazeu6cLLzvNXhuESS5LhMDQZjR0dHcOTuaDAQPveDvus9ToahB+/+AcADTtS/k1dDoyOx50zsVhgdDQaDfwHwIDhyZ2QkXDBGR4MPufTnzp3/HlwQlpBKvWLFqv4BL4q2JSauwDA7QRDJyStNphMAgJycHO4FJRDkZWWt5xhOSFhGUZTV2rJw4fPcq4s7RpOSTBBEd/cP3DPJ5XKNjga93oHu7h+4KmaxWJXKvRaLVSDIQ5ASs/kUjntw3AMAwHGPy+WKUD3ALRwvdhPK3PgL7qHmcDjj4hZs2pQ9b95T8fGLU1PXzTyfA/gHfa6uhsSa4LoAAAAASUVORK5CYII=" nextheight="534" nextwidth="569" class="image-node embed"><figcaption htmlattributes="[object Object]" class=""><em>Daily report of price paid for 100k gas, broken down by median, p75, p90 and p95 costs.</em></figcaption></figure><p>Besides fees, we also track transaction inclusion time and uptime. These are tangential to scaling, but degradation in these metrics could indicate that we need to scale more or focus on infra reliability. An active goal this quarter is to improve reliability so that 99.9% of blocks are built in less than 2 seconds, which we hope to achieve by landing the projects related to execution speed.&nbsp;</p><h2 id="h-looking-forward" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Looking Forward</h2><p>As we have observed fees starting to increase on Base Chain during periods of heightened activity, we are committed to accelerating our scaling roadmap to reduce these fees and maintain sub-second, sub-cent transactions on Base.</p><p>Our efforts to accelerate scaling have been made possible by the support of many teams across the ecosystem. We are grateful to the protocol teams and infrastructure operators that have been so receptive to and supportive of our scaling efforts.</p><p>We’ll continue to collaborate to scale Base Chain to support 1 billion+ users. If you’re interested in working on creative and ambitious scaling solutions to enable the next billion people to come onchain, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.base.org/jobs"><u>we’re hiring</u></a>, and we’d love to hear from you.</p><hr><p>Follow us on social to stay up to date with the latest: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/base">X</a> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/i/lists/1876354838289932418">Base team on X</a>) | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://warpcast.com/base/">Farcaster</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://discord.com/invite/buildonbase?utm_source=dotorg&amp;utm_medium=nav">Discord</a></p>]]></content:encoded>
            <author>base-engineering-blog@newsletter.paragraph.com (Anika Raghuvanshi)</author>
            <category>scaling</category>
            <category>infrastructure</category>
            <category>l2</category>
            <category>base</category>
            <category>blockchain</category>
            <category>sub-cent</category>
            <category>reth</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/ff00bb678383a1add193081d2823e39a00540c570188d248ecca9f08075a8dc1.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[OnchainTestKit: A Framework for End-to-End Testing of Onchain Applications]]></title>
            <link>https://blog.base.dev/introducing-onchaintestkit</link>
            <guid>p2ZgCnu73WmVSIncOdCG</guid>
            <pubDate>Mon, 29 Sep 2025 19:21:36 GMT</pubDate>
            <description><![CDATA[OnchainTestKit is a comprehensive end-to-end testing framework for onchain applications. This post outlines how developers can start using it today for reliable app testing.]]></description>
            <content:encoded><![CDATA[<h2 id="h-introduction" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Introduction</h2><p>Testing decentralized applications has always been fundamentally different from testing traditional web applications. While conventional apps deal with straightforward HTTP requests and predictable database states, onchain apps must navigate the complex world of blockchain interactions: wallet connections, transaction approvals, network switches, and chain state. Each of these interactions introduces unique challenges that existing testing frameworks weren't designed to handle.</p><p><br>We encountered many of these challenges when writing end-to-end tests for the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.coinbase.com/en-ca/blog/the-future-of-onchain-Liquidity-is-here-via-coinbase-verified-pools"><u>Verified Pools project</u></a>. This sophisticated DeFi application brought institutional-grade liquidity infrastructure to onchain markets, requiring testing of complex user flows across frontend, backend, and smart contract integrations. For example, a user connecting their wallet, approving token spending, signing Permit2 messages, providing liquidity to pools, and managing their positions across multiple contracts. What should have been a straightforward testing effort quickly became a weeks-long odyssey of technical challenges and productivity bottlenecks.</p><h2 id="h-the-core-challenges-of-e2e-onchain-testing" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Core Challenges of E2E Onchain Testing</h2><p>Testing decentralized applications introduces complexities that live at the intersection of web automation and blockchain interaction. Standard end-to-end testing frameworks are not inherently built for this environment, forcing developers to solve several fundamental problems before they can write effective tests.</p><p>Based on our experience, there are 4 major testing hurdles:</p><p><strong>1. Wallet Extension Setup</strong> Every test requires a browser wallet to be installed, configured, and funded from scratch. This process is unique for each wallet (like MetaMask or Coinbase Wallet) and can lead to brittle, custom scripts.</p><p><strong>2. Unpredictable Wallet Pop-ups</strong> During a test, user actions trigger wallet pop-ups for transaction approvals, message signing, and network switches. These pop-ups appear asynchronously with inconsistent timing and UI patterns, leading to unpredictability and flakiness.&nbsp;</p><p><strong>3. Shared On-Chain State</strong> True test parallelism is impossible on a shared blockchain. When multiple tests run at once, they use the same wallet addresses and interact with the same smart contracts on the same blockchain. This causes tests to interfere with each other's state, leading to unpredictable failures that are difficult to debug.</p><p><strong>4. Contract Deployment and State Management</strong> A fundamental tooling gap exists between smart contract development in Solidity (using Foundry) and end-to-end testing in TypeScript. Tests require contracts to be deployed with a specific initial state, but contract addresses are non-deterministic, which breaks the link with the frontend.</p><hr><h2 id="h-how-onchaintestkit-solves-each-problem" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">How OnchainTestKit Solves Each Problem</h2><p>OnchainTestKit directly addresses each of the four critical problems with purpose-built solutions:</p><h3 id="h-solution-1-automatic-wallet-management" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Solution 1: Automatic Wallet Management</h3><p>Instead of manually setting up each wallet type, OnchainTestKit handles everything automatically, with a unified interface that works for all wallet types. Here's an example of how to configure a test with Coinbase Wallet:</p><pre data-type="codeBlock" text="// Configure the test with a local node and a coinbase wallet
const coinbaseWalletConfig = configure()
&nbsp;&nbsp;.withLocalNode({
&nbsp;&nbsp;&nbsp;&nbsp;chainId: baseSepolia.id,
&nbsp;&nbsp;&nbsp;&nbsp;forkUrl: process.env.E2E_TEST_FORK_URL,
&nbsp;&nbsp;&nbsp;&nbsp;forkBlockNumber: BigInt(process.env.E2E_TEST_FORK_BLOCK_NUMBER ?? &quot;0&quot;),
&nbsp;&nbsp;&nbsp;&nbsp;hardfork: &quot;cancun&quot;,
&nbsp;&nbsp;})
&nbsp;&nbsp;.withCoinbase()
&nbsp;&nbsp;.withSeedPhrase({
&nbsp;&nbsp;&nbsp;&nbsp;seedPhrase: DEFAULT_SEED_PHRASE ?? &quot;&quot;,
&nbsp;&nbsp;&nbsp;&nbsp;password: DEFAULT_PASSWORD,
&nbsp;&nbsp;})
&nbsp;&nbsp;.withNetwork({
&nbsp;&nbsp;&nbsp;&nbsp;name: &quot;Base Sepolia&quot;,
&nbsp;&nbsp;&nbsp;&nbsp;chainId: baseSepolia.id,
&nbsp;&nbsp;&nbsp;&nbsp;symbol: &quot;ETH&quot;,
&nbsp;&nbsp;&nbsp;&nbsp;rpcUrl: &quot;http://localhost:8545&quot;,
&nbsp;&nbsp;})
&nbsp;&nbsp;.build();"><code><span class="hljs-comment">// Configure the test with a local node and a coinbase wallet</span>
const coinbaseWalletConfig <span class="hljs-operator">=</span> configure()
&nbsp;&nbsp;.withLocalNode({
&nbsp;&nbsp;&nbsp;&nbsp;chainId: baseSepolia.id,
&nbsp;&nbsp;&nbsp;&nbsp;forkUrl: process.env.E2E_TEST_FORK_URL,
&nbsp;&nbsp;&nbsp;&nbsp;forkBlockNumber: BigInt(process.env.E2E_TEST_FORK_BLOCK_NUMBER ?? <span class="hljs-string">"0"</span>),
&nbsp;&nbsp;&nbsp;&nbsp;hardfork: <span class="hljs-string">"cancun"</span>,
&nbsp;&nbsp;})
&nbsp;&nbsp;.withCoinbase()
&nbsp;&nbsp;.withSeedPhrase({
&nbsp;&nbsp;&nbsp;&nbsp;seedPhrase: DEFAULT_SEED_PHRASE ?? <span class="hljs-string">""</span>,
&nbsp;&nbsp;&nbsp;&nbsp;password: DEFAULT_PASSWORD,
&nbsp;&nbsp;})
&nbsp;&nbsp;.withNetwork({
&nbsp;&nbsp;&nbsp;&nbsp;name: <span class="hljs-string">"Base Sepolia"</span>,
&nbsp;&nbsp;&nbsp;&nbsp;chainId: baseSepolia.id,
&nbsp;&nbsp;&nbsp;&nbsp;symbol: <span class="hljs-string">"ETH"</span>,
&nbsp;&nbsp;&nbsp;&nbsp;rpcUrl: <span class="hljs-string">"http://localhost:8545"</span>,
&nbsp;&nbsp;})
&nbsp;&nbsp;.build();</code></pre><p><strong>Result</strong>: Developer-friendly, wallet agnostic, and easy to use. We hide all the complexity of wallet setup and management from the developer.</p><br><h3 id="h-solution-2-reliable-wallet-popup-handling" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Solution 2: Reliable Wallet Popup Handling</h3><p>OnchainTestKit provides a unified interface that abstracts all popup complexity:</p><pre data-type="codeBlock" text="// Instead of dealing with timing and popup detection:
await coinbaseWallet.handleAction(BaseActionType.CONNECT_TO_DAPP);
await coinbaseWallet.handleAction(BaseActionType.HANDLE_TRANSACTION, {
&nbsp;&nbsp;approvalType: ActionApprovalType.APPROVE
});

await coinbaseWallet.handleAction(BaseActionType.CHANGE_SPENDING_CAP, {
&nbsp;&nbsp;approvalType: ActionApprovalType.APPROVE
});"><code><span class="hljs-comment">// Instead of dealing with timing and popup detection:</span>
await coinbaseWallet.handleAction(BaseActionType.CONNECT_TO_DAPP);
await coinbaseWallet.handleAction(BaseActionType.HANDLE_TRANSACTION, {
&nbsp;&nbsp;approvalType: ActionApprovalType.APPROVE
});

await coinbaseWallet.handleAction(BaseActionType.CHANGE_SPENDING_CAP, {
&nbsp;&nbsp;approvalType: ActionApprovalType.APPROVE
});</code></pre><p><strong>Intelligent Waiting Strategies &amp; Retries</strong>: OnchainTestKit uses smart waiting strategies that understand blockchain timing patterns and wallet behavior. Built-in retry mechanisms handle transient failures automatically, while adaptive timeouts adjust based on network conditions and popup complexity.</p><p><strong>Result</strong>: Tests pass reliably. No more flaky tests due to timing issues or unpredictable wallet behavior.</p><br><h3 id="h-solution-3-true-test-isolation" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Solution 3: True Test Isolation</h3><p>OnchainTestKit provides each test with its own isolated Anvil blockchain node, eliminating all state conflicts:</p><pre data-type="codeBlock" text="// Each test gets its own LocalNodeManager
test('parallel test A', async ({ localNodeManager, smartContractManager }) =&gt; {
&nbsp;&nbsp;// Automatically starts Anvil node on available port (e.g., 10543)
&nbsp;&nbsp;// This test's transactions only affect its own blockchain
&nbsp;&nbsp;await page.click('#swap-button');
&nbsp;&nbsp;// Test logic...
});

test('parallel test B', async ({ localNodeManager, smartContractManager }) =&gt; {
&nbsp;&nbsp;// Automatically starts separate Anvil node on different port (e.g., 10847)
&nbsp;&nbsp;// Completely independent blockchain state
&nbsp;&nbsp;await page.click('#approve-button');
&nbsp;&nbsp;// Test logic...
});"><code><span class="hljs-comment">// Each test gets its own LocalNodeManager</span>
test(<span class="hljs-string">'parallel test A'</span>, async ({ localNodeManager, smartContractManager }) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
&nbsp;&nbsp;<span class="hljs-comment">// Automatically starts Anvil node on available port (e.g., 10543)</span>
&nbsp;&nbsp;<span class="hljs-comment">// This test's transactions only affect its own blockchain</span>
&nbsp;&nbsp;await page.click(<span class="hljs-string">'#swap-button'</span>);
&nbsp;&nbsp;<span class="hljs-comment">// Test logic...</span>
});

test(<span class="hljs-string">'parallel test B'</span>, async ({ localNodeManager, smartContractManager }) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
&nbsp;&nbsp;<span class="hljs-comment">// Automatically starts separate Anvil node on different port (e.g., 10847)</span>
&nbsp;&nbsp;<span class="hljs-comment">// Completely independent blockchain state</span>
&nbsp;&nbsp;await page.click(<span class="hljs-string">'#approve-button'</span>);
&nbsp;&nbsp;<span class="hljs-comment">// Test logic...</span>
});</code></pre><p><strong>How LocalNodeManager Works</strong>: OnchainTestKit automatically allocates available ports across processes and spins up isolated Anvil nodes for each test. It supports three different parallel testing strategies:</p><p><strong>1. Fork Existing Networks</strong>: Fork a testnet or mainnet at a specific block number without deploying contracts. Perfect for testing against existing protocol deployments:</p><pre data-type="codeBlock" text=".withLocalNode({
&nbsp;&nbsp;forkUrl: process.env.BASE_MAINNET_RPC_URL,
&nbsp;&nbsp;forkBlockNumber: process.env.E2E_TEST_FORK_BLOCK_NUMBER,
&nbsp;&nbsp;chainId: 8453
})"><code>.withLocalNode({
&nbsp;&nbsp;forkUrl: process.env.BASE_MAINNET_RPC_URL,
&nbsp;&nbsp;forkBlockNumber: process.env.E2E_TEST_FORK_BLOCK_NUMBER,
&nbsp;&nbsp;chainId: <span class="hljs-number">8453</span>
})</code></pre><p><strong>2. Clean Local State</strong>: Start with a fresh blockchain and deploy all dependent contracts. Ideal for testing new protocols or complex state setups:</p><pre data-type="codeBlock" text=".withLocalNode({
&nbsp;&nbsp;chainId: 84532,
&nbsp;&nbsp;// No fork - starts with clean state
})

// Then deploy contracts via smartContractManager"><code>.withLocalNode({
&nbsp;&nbsp;chainId: <span class="hljs-number">84532</span>,
&nbsp;&nbsp;<span class="hljs-comment">// No fork - starts with clean state</span>
})

<span class="hljs-comment">// Then deploy contracts via smartContractManager</span></code></pre><p><strong>3. Hybrid Approach</strong>: Fork a network and deploy additional test contracts on top. Combines real protocol state with custom test contracts:</p><pre data-type="codeBlock" text=".withLocalNode({
&nbsp;&nbsp;forkUrl: process.env.BASE_SEPOLIA_RPC_URL,
&nbsp;&nbsp;forkBlockNumber: 10_000_000n,
&nbsp;&nbsp;chainId: 84532
})

// Then deploy additional test contracts as needed"><code>.withLocalNode({
&nbsp;&nbsp;forkUrl: process.env.BASE_SEPOLIA_RPC_URL,
&nbsp;&nbsp;forkBlockNumber: 10_000_000n,
&nbsp;&nbsp;chainId: <span class="hljs-number">84532</span>
})

<span class="hljs-comment">// Then deploy additional test contracts as needed</span></code></pre><p>Each approach provides complete test isolation with independent blockchain state, allowing tests to manipulate time, account balances, and contract state without affecting other tests.</p><p><strong>Smart RPC Routing</strong>: One of the key innovations is automatic request interception. Your frontend can always use a fixed RPC URL like <code>localhost:8545</code>, and LocalNodeManager automatically routes these requests to the correct Anvil node for each test. No need to dynamically configure different ports—the framework handles the routing transparently.</p><p><strong>Automatic Cleanup</strong>: LocalNodeManager handles the complete node lifecycle, gracefully terminating each Anvil process after its test completes. This ensures no resource leaks or port conflicts, even when running large test suites with dozens of parallel nodes.</p><p><strong>Result</strong>: Fully parallelized testing with no coordination overhead. CI times drop from hours to minutes.</p><br><h3 id="h-solution-4-deterministic-contract-deployments" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Solution 4: Deterministic Contract Deployments</h3><p>OnchainTestKit bridges the Solidity/TypeScript gap using CREATE2 for deterministic contract deployments:</p><pre data-type="codeBlock" text="await smartContractManager.setContractState({
&nbsp;&nbsp;deployments: [
&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name: 'MockUSDC',&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;salt: '0x1234...', // CREATE2 salt for deterministic address
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;deployer: admin,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;args: ['USD Coin', 'USDC', 6]&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;},
&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name: 'DEXContract',&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;salt: '0x5678...',&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;deployer: admin,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;args: [mockUsdcAddress]&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;],
&nbsp;&nbsp;calls: [
&nbsp;&nbsp;&nbsp;&nbsp;{ target: mockUsdcAddress, functionName: 'mint', args: [user, amount], account: admin },
&nbsp;&nbsp;&nbsp;&nbsp;{ target: mockUsdcAddress, functionName: 'approve', args: [dexAddress, amount], account: user }
&nbsp;&nbsp;]
});"><code>await smartContractManager.<span class="hljs-title function_ invoke__">setContractState</span>({
<span class="hljs-attr">&nbsp;&nbsp;deployments</span>: [
&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;
<span class="hljs-attr">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name</span>: <span class="hljs-string">'MockUSDC'</span>,&nbsp;
<span class="hljs-attr">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;salt</span>: <span class="hljs-string">'0x1234...'</span>, // CREATE2 salt <span class="hljs-keyword">for</span> deterministic address
<span class="hljs-attr">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;deployer</span>: admin,
<span class="hljs-attr">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;args</span>: [<span class="hljs-string">'USD Coin'</span>, <span class="hljs-string">'USDC'</span>, <span class="hljs-number">6</span>]&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;},
&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;
<span class="hljs-attr">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name</span>: <span class="hljs-string">'DEXContract'</span>,&nbsp;
<span class="hljs-attr">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;salt</span>: <span class="hljs-string">'0x5678...'</span>,&nbsp;
<span class="hljs-attr">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;deployer</span>: admin,
<span class="hljs-attr">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;args</span>: [mockUsdcAddress]&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;],
<span class="hljs-attr">&nbsp;&nbsp;calls</span>: [
&nbsp;&nbsp;&nbsp;&nbsp;{ <span class="hljs-attr">target</span>: mockUsdcAddress, <span class="hljs-attr">functionName</span>: <span class="hljs-string">'mint'</span>, <span class="hljs-attr">args</span>: [user, amount], <span class="hljs-attr">account</span>: admin },
&nbsp;&nbsp;&nbsp;&nbsp;{ <span class="hljs-attr">target</span>: mockUsdcAddress, <span class="hljs-attr">functionName</span>: <span class="hljs-string">'approve'</span>, <span class="hljs-attr">args</span>: [dexAddress, amount], <span class="hljs-attr">account</span>: user }
&nbsp;&nbsp;]
});</code></pre><br><p><strong>How CREATE2 Works</strong>: OnchainTestKit uses CREATE2 deployment with fixed salts to ensure contracts always deploy to the same addresses. The SmartContractManager predicts deployment addresses before deployment, checks if contracts already exist at those addresses, and loads Foundry artifacts automatically from your out/ artifact directory. This creates a seamless bridge between your Solidity contracts and TypeScript tests.</p><p><strong>Foundry Integration</strong>: Automatically loads compiled contract artifacts, eliminating manual ABI management or deployment script coordination between contract and test teams.</p><p><strong>Result</strong>: Reliable contract testing with predictable addresses. Full user journeys from smart contract interaction to UI feedback work consistently across all test runs.</p><hr><h2 id="h-how-we-use-onchaintestkit-in-verified-pools" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">How We Use OnchainTestKit in Verified Pools</h2><p>Here's a real example from our Verified Pools project showing how OnchainTestKit transforms complex onchain app testing into clean, readable tests:</p><pre data-type="codeBlock" text="// walletConfig/metamaskWalletConfig.ts
import { baseSepolia } from 'viem/chains';
import { configure } from '@coinbase/onchaintestkit';

export const DEFAULT_PASSWORD = 'PASSWORD';
export const DEFAULT_SEED_PHRASE = process.env.E2E_TEST_SEED_PHRASE;

// Reusable configuration for MetaMask tests with Base Sepolia fork
const metamaskConfig = configure()
&nbsp;&nbsp;.withLocalNode({
&nbsp;&nbsp;&nbsp;&nbsp;chainId: baseSepolia.id,
&nbsp;&nbsp;&nbsp;&nbsp;forkUrl: process.env.E2E_TEST_FORK_URL,
&nbsp;&nbsp;&nbsp;&nbsp;forkBlockNumber: BigInt(process.env.E2E_TEST_FORK_BLOCK_NUMBER ?? '0'),
&nbsp;&nbsp;&nbsp;&nbsp;hardfork: 'cancun',
&nbsp;&nbsp;})
&nbsp;&nbsp;.withMetaMask()
&nbsp;&nbsp;.withSeedPhrase({
&nbsp;&nbsp;&nbsp;&nbsp;seedPhrase: DEFAULT_SEED_PHRASE ?? '',
&nbsp;&nbsp;&nbsp;&nbsp;password: DEFAULT_PASSWORD,
&nbsp;&nbsp;})
&nbsp;&nbsp;.withNetwork({
&nbsp;&nbsp;&nbsp;&nbsp;name: 'Base Sepolia',
&nbsp;&nbsp;&nbsp;&nbsp;chainId: baseSepolia.id,
&nbsp;&nbsp;&nbsp;&nbsp;symbol: 'ETH',
&nbsp;&nbsp;&nbsp;&nbsp;rpcUrl: 'http://localhost:8545', // Fixed URL, auto-routed to correct port
&nbsp;&nbsp;})
&nbsp;&nbsp;.build();

export { metamaskConfig };"><code><span class="hljs-comment">// walletConfig/metamaskWalletConfig.ts</span>
<span class="hljs-keyword">import</span> { <span class="hljs-title">baseSepolia</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'viem/chains'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">configure</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'@coinbase/onchaintestkit'</span>;

export const DEFAULT_PASSWORD <span class="hljs-operator">=</span> <span class="hljs-string">'PASSWORD'</span>;
export const DEFAULT_SEED_PHRASE <span class="hljs-operator">=</span> process.env.E2E_TEST_SEED_PHRASE;

<span class="hljs-comment">// Reusable configuration for MetaMask tests with Base Sepolia fork</span>
const metamaskConfig <span class="hljs-operator">=</span> configure()
&nbsp;&nbsp;.withLocalNode({
&nbsp;&nbsp;&nbsp;&nbsp;chainId: baseSepolia.id,
&nbsp;&nbsp;&nbsp;&nbsp;forkUrl: process.env.E2E_TEST_FORK_URL,
&nbsp;&nbsp;&nbsp;&nbsp;forkBlockNumber: BigInt(process.env.E2E_TEST_FORK_BLOCK_NUMBER ?? <span class="hljs-string">'0'</span>),
&nbsp;&nbsp;&nbsp;&nbsp;hardfork: <span class="hljs-string">'cancun'</span>,
&nbsp;&nbsp;})
&nbsp;&nbsp;.withMetaMask()
&nbsp;&nbsp;.withSeedPhrase({
&nbsp;&nbsp;&nbsp;&nbsp;seedPhrase: DEFAULT_SEED_PHRASE ?? <span class="hljs-string">''</span>,
&nbsp;&nbsp;&nbsp;&nbsp;password: DEFAULT_PASSWORD,
&nbsp;&nbsp;})
&nbsp;&nbsp;.withNetwork({
&nbsp;&nbsp;&nbsp;&nbsp;name: <span class="hljs-string">'Base Sepolia'</span>,
&nbsp;&nbsp;&nbsp;&nbsp;chainId: baseSepolia.id,
&nbsp;&nbsp;&nbsp;&nbsp;symbol: <span class="hljs-string">'ETH'</span>,
&nbsp;&nbsp;&nbsp;&nbsp;rpcUrl: <span class="hljs-string">'http://localhost:8545'</span>, <span class="hljs-comment">// Fixed URL, auto-routed to correct port</span>
&nbsp;&nbsp;})
&nbsp;&nbsp;.build();

export { metamaskConfig };</code></pre><br><p>This test covers a complete user journey in Verified Pools:</p><ol><li><p>Connect MetaMask wallet to the onchain app</p></li><li><p>Navigate to the swap interface</p></li><li><p>Enter swap amount (0.0001 ETH)</p></li><li><p>Execute the swap transaction</p></li><li><p>Handle all required wallet popups (spending cap approval, Permit2 signature, transaction confirmation)</p></li><li><p>Verify the swap completed successfully<br></p></li></ol><pre data-type="codeBlock" text="// swap.spec.ts

import { createOnchainTest } from '@coinbase/onchaintestkit';
import { NotificationPageType } from '@coinbase/onchaintestkit/wallets/MetaMask';
import { ActionApprovalType, BaseActionType } from '@coinbase/onchaintestkit/wallets/BaseWallet';
import { metamaskConfig } from './walletConfig/metamaskWalletConfig';

const test = createOnchainTest(metamaskConfig);
const { expect } = test;

test.describe('Verified Pools Swap', () =&gt; {
&nbsp;&nbsp;test('connect wallet and swap @tx', async ({ page, metamask }) =&gt; {
&nbsp;&nbsp;&nbsp;&nbsp;if (!metamask) throw new Error('MetaMask fixture is required');

&nbsp;&nbsp;&nbsp;&nbsp;// Navigate to swap interface
&nbsp;&nbsp;&nbsp;&nbsp;await page.goto('/swap');

&nbsp;&nbsp;&nbsp;&nbsp;// Connect wallet - OnchainTestKit handles all the complexity
&nbsp;&nbsp;&nbsp;&nbsp;await page.getByTestId('ockConnectButton').first().click();
&nbsp;&nbsp;&nbsp;&nbsp;await page.getByTestId('ockModalOverlay')
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.first()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.getByRole('button', { name: 'MetaMask' })
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.click();
&nbsp;&nbsp;&nbsp;&nbsp;await metamask.handleAction(BaseActionType.CONNECT_TO_DAPP);
&nbsp;&nbsp;&nbsp;&nbsp;await page.getByRole('button', { name: /^Accept$/ }).click();

&nbsp;&nbsp;&nbsp;&nbsp;// Set up the swap
&nbsp;&nbsp;&nbsp;&nbsp;const inputField = page.locator('input[placeholder=&quot;0.0&quot;]').first();
&nbsp;&nbsp;&nbsp;&nbsp;await inputField.fill('0.0001');

&nbsp;&nbsp;&nbsp;&nbsp;// Execute swap
&nbsp;&nbsp;&nbsp;&nbsp;await page.getByRole('button', { name: 'Swap' }).click();
&nbsp;&nbsp;&nbsp;&nbsp;await page.getByRole('button', { name: 'Confirm' }).click();

&nbsp;&nbsp;&nbsp;&nbsp;// Handle spending cap approval (Permit2)
&nbsp;&nbsp;&nbsp;&nbsp;let notificationType = await metamask.identifyNotificationType();
&nbsp;&nbsp;&nbsp;&nbsp;if (notificationType === NotificationPageType.SpendingCap) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;await metamask.handleAction(BaseActionType.CHANGE_SPENDING_CAP, {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;approvalType: ActionApprovalType.APPROVE,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;notificationType = await metamask.identifyNotificationType();
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;// Handle signature for Permit2
&nbsp;&nbsp;&nbsp;&nbsp;if (notificationType === NotificationPageType.SpendingCap) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;await metamask.handleAction(BaseActionType.HANDLE_SIGNATURE, {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;approvalType: ActionApprovalType.APPROVE,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;notificationType = await metamask.identifyNotificationType();
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;// Handle the actual swap transaction
&nbsp;&nbsp;&nbsp;&nbsp;if (notificationType === NotificationPageType.Transaction) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;await metamask.handleAction(BaseActionType.HANDLE_TRANSACTION, {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;approvalType: ActionApprovalType.APPROVE,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;// Verify swap completion
&nbsp;&nbsp;&nbsp;&nbsp;await expect(page.getByRole('link', { name: 'View on Explorer' })).toBeVisible({
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timeout: 10_000,
&nbsp;&nbsp;&nbsp;&nbsp;});
&nbsp;&nbsp;});
});
"><code><span class="hljs-comment">// swap.spec.ts</span>

<span class="hljs-keyword">import</span> { <span class="hljs-title">createOnchainTest</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'@coinbase/onchaintestkit'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">NotificationPageType</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'@coinbase/onchaintestkit/wallets/MetaMask'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ActionApprovalType</span>, <span class="hljs-title">BaseActionType</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'@coinbase/onchaintestkit/wallets/BaseWallet'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">metamaskConfig</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'./walletConfig/metamaskWalletConfig'</span>;

const test <span class="hljs-operator">=</span> createOnchainTest(metamaskConfig);
const { expect } <span class="hljs-operator">=</span> test;

test.describe(<span class="hljs-string">'Verified Pools Swap'</span>, () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
&nbsp;&nbsp;test(<span class="hljs-string">'connect wallet and swap @tx'</span>, async ({ page, metamask }) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>metamask) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'MetaMask fixture is required'</span>);

&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment">// Navigate to swap interface</span>
&nbsp;&nbsp;&nbsp;&nbsp;await page.goto(<span class="hljs-string">'/swap'</span>);

&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment">// Connect wallet - OnchainTestKit handles all the complexity</span>
&nbsp;&nbsp;&nbsp;&nbsp;await page.getByTestId(<span class="hljs-string">'ockConnectButton'</span>).first().click();
&nbsp;&nbsp;&nbsp;&nbsp;await page.getByTestId(<span class="hljs-string">'ockModalOverlay'</span>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.first()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.getByRole(<span class="hljs-string">'button'</span>, { name: <span class="hljs-string">'MetaMask'</span> })
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.click();
&nbsp;&nbsp;&nbsp;&nbsp;await metamask.handleAction(BaseActionType.CONNECT_TO_DAPP);
&nbsp;&nbsp;&nbsp;&nbsp;await page.getByRole(<span class="hljs-string">'button'</span>, { name: <span class="hljs-operator">/</span><span class="hljs-operator">^</span>Accept$<span class="hljs-operator">/</span> }).click();

&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment">// Set up the swap</span>
&nbsp;&nbsp;&nbsp;&nbsp;const inputField <span class="hljs-operator">=</span> page.locator(<span class="hljs-string">'input[placeholder="0.0"]'</span>).first();
&nbsp;&nbsp;&nbsp;&nbsp;await inputField.fill(<span class="hljs-string">'0.0001'</span>);

&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment">// Execute swap</span>
&nbsp;&nbsp;&nbsp;&nbsp;await page.getByRole(<span class="hljs-string">'button'</span>, { name: <span class="hljs-string">'Swap'</span> }).click();
&nbsp;&nbsp;&nbsp;&nbsp;await page.getByRole(<span class="hljs-string">'button'</span>, { name: <span class="hljs-string">'Confirm'</span> }).click();

&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment">// Handle spending cap approval (Permit2)</span>
&nbsp;&nbsp;&nbsp;&nbsp;let notificationType <span class="hljs-operator">=</span> await metamask.identifyNotificationType();
&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword">if</span> (notificationType <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> NotificationPageType.SpendingCap) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;await metamask.handleAction(BaseActionType.CHANGE_SPENDING_CAP, {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;approvalType: ActionApprovalType.APPROVE,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;notificationType <span class="hljs-operator">=</span> await metamask.identifyNotificationType();
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment">// Handle signature for Permit2</span>
&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword">if</span> (notificationType <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> NotificationPageType.SpendingCap) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;await metamask.handleAction(BaseActionType.HANDLE_SIGNATURE, {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;approvalType: ActionApprovalType.APPROVE,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;notificationType <span class="hljs-operator">=</span> await metamask.identifyNotificationType();
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment">// Handle the actual swap transaction</span>
&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-keyword">if</span> (notificationType <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> NotificationPageType.Transaction) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;await metamask.handleAction(BaseActionType.HANDLE_TRANSACTION, {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;approvalType: ActionApprovalType.APPROVE,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-comment">// Verify swap completion</span>
&nbsp;&nbsp;&nbsp;&nbsp;await expect(page.getByRole(<span class="hljs-string">'link'</span>, { name: <span class="hljs-string">'View on Explorer'</span> })).toBeVisible({
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timeout: <span class="hljs-number">10_000</span>,
&nbsp;&nbsp;&nbsp;&nbsp;});
&nbsp;&nbsp;});
});
</code></pre><p><strong>What This Shows:</strong></p><ul><li><p><strong>No Custom Wallet Setup</strong>: OnchainTestKit handles MetaMask installation and configuration automatically</p></li><li><p><strong>Fork Testing</strong>: Tests run against real Base Sepolia network at a specific block number where all dependent contracts are deployed, but each test gets its own isolated fork</p></li><li><p><strong>Reliable Popup Handling</strong>: Complex wallet interactions reduced to simple handleAction calls</p></li><li><p><strong>Smart RPC Routing</strong>: Frontend uses fixed <code>localhost:8545</code>, framework routes to correct test node</p></li><li><p><strong>Automatic Cleanup</strong>: No manual resource management needed</p></li></ul><p>This same test would have required hundreds of lines of custom wallet automation code before OnchainTestKit. Now it's clean, readable, and maintainable.</p><br><h3 id="h-production-cicd-with-onchaintestkit" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Production CI/CD with OnchainTestKit</h3><p>Here's how we run these tests at scale in our Verified Pools CI pipeline:</p><pre data-type="codeBlock" text="# .github/workflows/playwright.yml

jobs:
&nbsp;&nbsp;e2e-tests:
&nbsp;&nbsp;&nbsp;&nbsp;# additional setup steps omitted for brevity
&nbsp;&nbsp;&nbsp;&nbsp;# Install xvfb for headless browser testing

&nbsp;&nbsp;&nbsp;&nbsp;- name: Install xvfb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;run: |
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sudo apt-get update
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sudo apt-get install -y xvfb

&nbsp;&nbsp;&nbsp;&nbsp;- name: Install dependencies
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;run: yarn install --frozen-lockfile

&nbsp;&nbsp;&nbsp;&nbsp;- name: Install Playwright Browsers
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;run: yarn playwright install --with-deps

&nbsp;&nbsp;&nbsp;&nbsp;- name: Build application
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;run: yarn build
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;env:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NEXT_PUBLIC_BASE_SEPOLIA_RPC_URLS: http://localhost:8545

&nbsp;&nbsp;&nbsp;&nbsp;- name: Prepare MetaMask Extension
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;run: yarn e2e:metamask:prepare

&nbsp;&nbsp;&nbsp;&nbsp;# Run transaction tests in parallel with 10 workers
&nbsp;&nbsp;&nbsp;&nbsp;- name: Run Playwright TX tests with xvfb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;run: xvfb-run --auto-servernum --server-args=&quot;-screen 0 1280x960x24&quot; yarn playwright test --workers=10 --reporter=list,github
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;env:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;E2E_TEST_SEED_PHRASE: ${{ secrets.E2E_TEST_SEED_PHRASE }}"><code># .github/workflows<span class="hljs-operator">/</span>playwright.yml

jobs:
&nbsp;&nbsp;e2e<span class="hljs-operator">-</span>tests:
&nbsp;&nbsp;&nbsp;&nbsp;# additional setup steps omitted <span class="hljs-keyword">for</span> brevity
&nbsp;&nbsp;&nbsp;&nbsp;# Install xvfb <span class="hljs-keyword">for</span> headless browser testing

&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-operator">-</span> name: Install xvfb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;run: <span class="hljs-operator">|</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sudo apt<span class="hljs-operator">-</span>get update
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sudo apt<span class="hljs-operator">-</span>get install <span class="hljs-operator">-</span>y xvfb

&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-operator">-</span> name: Install dependencies
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;run: yarn install <span class="hljs-operator">-</span><span class="hljs-operator">-</span>frozen<span class="hljs-operator">-</span>lockfile

&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-operator">-</span> name: Install Playwright Browsers
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;run: yarn playwright install <span class="hljs-operator">-</span><span class="hljs-operator">-</span>with<span class="hljs-operator">-</span>deps

&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-operator">-</span> name: Build application
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;run: yarn build
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;env:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NEXT_PUBLIC_BASE_SEPOLIA_RPC_URLS: http:<span class="hljs-comment">//localhost:8545</span>

&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-operator">-</span> name: Prepare MetaMask Extension
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;run: yarn e2e:metamask:prepare

&nbsp;&nbsp;&nbsp;&nbsp;# Run transaction tests in parallel with <span class="hljs-number">10</span> workers
&nbsp;&nbsp;&nbsp;&nbsp;<span class="hljs-operator">-</span> name: Run Playwright TX tests with xvfb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;run: xvfb<span class="hljs-operator">-</span>run <span class="hljs-operator">-</span><span class="hljs-operator">-</span>auto<span class="hljs-operator">-</span>servernum <span class="hljs-operator">-</span><span class="hljs-operator">-</span>server<span class="hljs-operator">-</span>args<span class="hljs-operator">=</span><span class="hljs-string">"-screen 0 1280x960x24"</span> yarn playwright test <span class="hljs-operator">-</span><span class="hljs-operator">-</span>workers<span class="hljs-operator">=</span><span class="hljs-number">10</span> <span class="hljs-operator">-</span><span class="hljs-operator">-</span>reporter<span class="hljs-operator">=</span>list,github
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;env:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;E2E_TEST_SEED_PHRASE: ${{ secrets.E2E_TEST_SEED_PHRASE }}</code></pre><br><p><strong>Key Production Features:</strong></p><ul><li><p><strong>Parallel Execution</strong>: --workers=10 runs 10 tests simultaneously, each with isolated blockchain nodes</p></li><li><p><strong>Environment Isolation</strong>: Each test gets its own fork of Base Sepolia without conflicts</p></li><li><p><strong>Robust CI Setup</strong>: Handles headless browser automation with xvfb and proper cleanup</p></li></ul><p><strong>Results</strong>: Our CI runs 100+ comprehensive onchain app tests in parallel in under 10 minutes.</p><hr><h2 id="h-try-onchaintestkit-today" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Try OnchainTestKit Today</h2><p>OnchainTestKit is available now and has been published to NPM. To get started, check out the documentation and examples in the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/coinbase/onchaintestkit"><u>GitHub repository</u></a>.</p><h2 id="h-interested-in-improving-onchain-developer-experience" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Interested in Improving Onchain Developer Experience?</h2><p>If increasing the impact of onchain developers piques your interest, please know that our Onchain DevX team would love to meet you! <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.coinbase.com/careers/positions/7081419">We're Hiring</a>.</p>]]></content:encoded>
            <author>base-engineering-blog@newsletter.paragraph.com (Timothy Wang)</author>
            <author>base-engineering-blog@newsletter.paragraph.com (Matthew Bunday)</author>
            <category>testing</category>
            <category>devx</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/0d74024a994a89e6c2a5d129aacc313de32018e4240f5e7137b32793441ba328.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Flashblocks Deep Dive: How we made Base 10x faster]]></title>
            <link>https://blog.base.dev/flashblocks-deep-dive</link>
            <guid>Unm7vkJ9Mn9HUiAmMzA1</guid>
            <pubDate>Tue, 09 Sep 2025 20:33:19 GMT</pubDate>
            <description><![CDATA[In this post, Cody from the Base Chain team provides a deep dive into block building ]]></description>
            <content:encoded><![CDATA[<p>To build an open network for the global economy, we’re focused on making Base Chain faster and cheaper with sub-cent, sub-second transactions. And earlier this year, we shipped <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/buildonbase/status/1945618401013239880"><u>Flashblocks on Base mainnet</u></a>, making Base 10x faster by reducing effective block times from 2 seconds to just 200 milliseconds.</p><p>Launched on July 16th, this concluded a four-month development journey from testnet to mainnet. We built in public, and wanted to share more of the technical details of Flashblocks, the challenges we encountered during our journey, and our learnings. If you're looking for support on integrating your app or infrastructure provider with Flashblocks, we recommend checking out <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.base.dev/accelerating-base-with-flashblocks"><u>this blog post</u></a> and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.base.org/base-chain/flashblocks/docs"><u>FAQ page</u></a>.A </p><h1 id="h-a-primer-on-flashblocks" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">A Primer on Flashblocks</h1><p>Previously, Base produced a new block every 2 seconds. With Flashblocks (built with <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://flashbots"><u>Flashbots</u></a>), the builder streams 200 ms sub-blocks during that 2-second window, giving sub-second preconfirmations so transactions feel instant.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/cda46a693f50674cb428fd1f7e03e3786c655689953ba869f687112d1396cc2d.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAANCAIAAABHKvtLAAAACXBIWXMAAAsTAAALEwEAmpwYAAADR0lEQVR4nK2Ty08bRxjAt2oPJS0lRBEYXLWlFhvsNTFOnQHbC3jxY73s2sDGZtf7ts2u8euQ9sKlm0f/hKIIiVhO1CAs5Etzyl4q3ziZv4Cb2yhw25xQSOJKuDWO4t7600gzo3n89M18H9S+4OjoqF6vP3/+e6c1Gn/0jts9NBqN7pJhvDCMF91ps9lsfwTU6UqlMuK4tbaWSiZF+Mb0J58O8kJ2ZZVFF3AMC52cvOpsM00zGiX9/hBJJRkmMzRkGR+3rdHCGi24XF5ZVtrt930Eb9+9fXD/wRKGc6ysSOosQL/9xqZIKsfKceqOwIutVquz+/T0NKepqxSZUeTCpnbTiQDPD7lsJptW4uSyruuaplmtVrfbXSwWP4hA13XMv6RwmQ1JW/AFJr+bLGyUJFahqUSvwDRf4xEMga+FF+wUPgNB0OhXEBlyhRfsdtuwIkum+dr8l0vB8fHx1tZWaD6YFTKbSg4F/rFrlk0ll+HSiVhC4IWuoNlsYotL0zduobcDkcXlz6EvRwYtkUUi4A3CE3aBF/o/EYIgBEHQZExiOE1K+2/Pfj1i2RAVKcmuhJdF4TKCgYEBVVVZns2X8nd/uuu86ZzzzhXLpUK5QCdpXddlWYZhGAAgy/LZ2dk/AsMwyuUyNucTE8k0m/I4naNDV7McLyaSVDDIM8yrk5OOoLZfQwPo6JTFgTo8EQ90wQw2g6DTI/CopEjnb877Z5Gu/xxEnSpP5GVqHkxOjF8pKvEsRyRIr8AlWq0/u1kUCUdmplzYbICYx69+NnT9i+vR+SiOhhw2u8CLZz1cCs7fnOu6voT7GJ5Kq+tuYB8ZG8wVeYanVu6EBDH18q+XPVmkcXQyn1V/LJRcdoffA0obuXxWpcnY/Xv3REmyWq0AgFQqdflE7XY7X8hPAQcpxFbT9PcuG3QFSmjrUWbZS/gC4UBXYJomSUQDs4CNx9Pr62PDw1MTEwrLMKsxn9utKPK79/0+uVPJtf3a0ydPH20/quzuPqlWt3/druzu1vZrhmH0HjAM4+Dg4Ldnz3Z2dqrV6uNK5XGlcnDB4eHhf1Zyh729PQAAjuMIgsAw/PCXhx8f6ARer9fdF8Aw3K2pvnwg6L3i/+Jv20j18mE9hOYAAAAASUVORK5CYII=" nextheight="540" nextwidth="1300" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>There are 10 Flashblocks per block, and each of them (flashblock_i) can contain up to i/10 of the regular block’s total gas budget. A series of Flashblocks are combined to recreate a full block.</p><p>Now that we've covered what Flashblocks are, let's dive into how we implemented them into Base Chain.</p><h1 id="h-sequencer-architecture" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Sequencer Architecture</h1><p>As part of the Flashblocks launch, we added several new infrastructure components to the sequencer. To contextualize these changes, we share what the sequencer architecture looked like both before and after Flashblocks.</p><h3 id="h-architecture-before-flashblocks" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Architecture before Flashblocks</h3><p>At Base, we operate a high availability sequencer system, with five sequencer instances.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e7a73e39f834f347c37a3279eaae030c98cc95d7c16e6e4092bb3fa828e88f57.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAPCAIAAAAK4lpAAAAACXBIWXMAAAsTAAALEwEAmpwYAAADBElEQVR4nFVUoZakMBDkB+ZnVqzYX1ixZsWYEWMixiAwGAQmJgaBwkTExMRgYtrExGBiUBgMJmZUDCb3hprj3ZUK/aCrU1VNkf8ipWStdc4RkbV2PKAPKKWklOoADubAOI44WGvpgLV2nuec877vaFucBOu6juM4z/N0wDnnvXfOjeNIRH3fX6/Xsiyv12tVVfYAOqK1cy6EMM8zEZ093wRgizFiQOccbtAd4JxrrY0xQgjGmJRSaz0MA2Os6zpjjNZ6HMe2bT8/PxljwzCAeNu2/24QY5RSPp/Ps7IsCxGllPC4bdv5uO87EW3bFmPEfCkl771Sqm1ba+00TTHGN4GUsixLzvkpolKqruuu66SUkNgY07Yt5/xU31pb1zXuYa313k/T1Pc9BAwhENG+70VKqW3b2+3GOYexxpi+7xljVVWN4+ick1IKIe73uxCCiIwxwzB0XVeWZVVV8Mlaq5S63+91XRPROI5SyhjjmyDG6Jxb1xXd4fA0TSEE770QAsVpmrz3IQTOedu2IYRlWdwBGIMm1toYI2Ps+XwW+763bbuuq9Z6WRat9TkmEXnviaiqKiEE/FRKQZyyLCEdUouRcR6GYZqmx+PxIng+n0gnzCEizjnGFEJorWOMdV1zzvd9hysxxupASklr3TQNBDDGhBCMMYwxRGbbtheBcw7fENGyLE3TeO9TSnVdSylzzk3TcM4RB2ttzhkEOWelFGMs5wwn0E0Ise+71vpFkFLq+94Yg00ZhgExMMZg46y1VVU1TYONw6o/Ho/zNWMMwgYNYQk24yVRSgl3VEohoGd4kI0QQl3XsBQ5ds4hP5gAxKc9IQQI+3g8UkrvFEkpsfHDAViHxdZa9weGYVBK4YWu6/DYNE1ZlogA7G2ahjEmhFBKvU3WWl8ul+JAWZYY/Pv7G5Xb7YaQfH19ofLz84PK/X4viuJyufz+/v5bKYri4+PDWvu6Qc4ZynRd1/c9EcUY8eND/I0x8QDCrrXGT2LbNuzKPM8hhHVdY4xYFNTXdc05/wGRbN1fEBT8xwAAAABJRU5ErkJggg==" nextheight="767" nextwidth="1600" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Each sequencer is made up&nbsp; of the following infrastructure components:</p><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/ethereum-optimism/optimism/tree/develop/op-node"><strong><u>Op-node</u></strong></a>: the standard op-stack consensus layer (CL) software</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/ethereum-optimism/op-geth"><strong><u>Op-geth</u></strong></a>: the standard op-stack execution layer (EL) software</p><ul><li><p>op-node and op-geth communicate via <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://hackmd.io/@danielrachi/engine_api"><u>Engine APIs</u></a> for block building.&nbsp;</p></li></ul></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/ethereum-optimism/optimism/tree/develop/op-conductor"><strong><u>Op-conductor</u></strong></a>: the high availability control component, with a raft consensus for leadership election</p></li></ul><p>One sequencer instance functions as the “leader” and is responsible for sequencing by building blocks and propagating them via P2P to other nodes. The remaining four sequencers act as follower Base nodes that synchronize the chain. Leadership transfer occurs if the current leader stops block production within a specified threshold.</p><h3 id="h-architecture-with-flashblocks" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Architecture With Flashblocks</h3><p>With Flashblocks, we integrated several new components within each single sequencer in the high availability system, to allow for the addition of more custom block building functionality. These include:</p><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/flashbots/rollup-boost"><strong><u>Rollup-boost</u></strong></a> (CL&lt;&gt;EL Engine API proxy)</p><ul><li><p><strong>Why it’s needed for Flashblocks:</strong> Enables sharing Flashblocks with the EL without modifying the CL, by adding a controlled interception point on Engine API calls. It localizes risk and lets us experiment off-protocol</p></li><li><p><strong>What it unlocks:</strong> A stable seam for future block-building evolutions (e.g. multi-builder), without refactoring CL/EL</p></li></ul></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/flashbots/op-rbuilder"><strong><u>Op-rbuilder</u></strong></a><strong> </strong>(out-of-protocol builder @ 200ms cadence)</p><ul><li><p><strong>Why&nbsp; it’s needed for Flashblocks:</strong> Produces the sub-second Flashblocks, decoupled from the EL</p></li><li><p><strong>What it unlocks:</strong> A pluggable builder surface for new block building mechanisms (e.g. MEV-aware strategies, auctions across multiple builders)</p></li></ul></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/flashbots/rollup-boost/tree/main/crates/websocket-proxy"><strong><u>Websocket proxy</u></strong></a><strong> </strong>(Flashblocks stream fan-out)</p><ul><li><p><strong>Why it’s needed for Flashblocks:</strong> A broadcast layer such that many consumers can read the Flashblocks stream without DoS-ing the builder</p></li></ul></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/node-reth"><strong><u>Node-reth</u></strong></a><strong> </strong>(RPC surface exposing preconfirmations)</p><ul><li><p><strong>Why it’s needed for Flashblocks:</strong> Converts the streamed Flashblocks into familiar RPCs so apps and wallets can consume preconfirmation state without new SDKs or protocol changes</p></li></ul></li></ul><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b9c500958c116436037933fd044aefb19f24713e1210447918e95fcfd46c7934.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAVCAIAAACor3u9AAAACXBIWXMAAAsTAAALEwEAmpwYAAADbklEQVR4nK2Vz28TRxTH93+oAuIPaC3UOBd8a3swlSIXbkhRVYoEJYcmLqHErYSWA9rQNnap8LaV1VWbhFUiomkiPC4/MlWArFMahoDjBUyyuIA3aWRv7dqeOJSsHcnLoHjIYigcgvlqDzvztO/z3rw3bzlKqSzLkiRBCAEAUk1sR5ZlAIC8Lk3T6MbFEUI8Hg+3LkmSKKWEkHKdKKUYY2aqLR/Veah/t3fYUwMYhgEhtJ2+LBBN02RZppSa5spqpfKc1Y7jBRnoug4hNAxD0zS9JlVVTXOlYj4sLRULuQwhOctanZ2b6x/oZ76MRb2QyxT/zRZymQclYke2Wqnks+nM3/fm784ai6kHJfIEgBACALzd3Ox0Oh0OR3t7OyFF0OcfCvWEvjn09Re7lbHhs6eHD3buSy8kFTT6fY/X+9G7R7t2Hdq7fWZqvLW1leO4lpaWG9evnPtVCvV2D4V6QJ9/5soFSh89BXg8nm3bXE6nk+f5hXm978Th8GAwisDxI/vPj/w8eurkp598mF5IDv/in0Qj4cFg18fvRRG4NjWRvHNHXdPNq5cvhgeDR7t2/eTvPn5kP5448wQAIUylUn39A++4tn73bW84DE1zJZ9NJxPTcOiHfDZtmsuxeDwQCLAjujsbTyamb8Wit2LRQi5jH3fFfJjPppeXCvlsupDLMNMaAABAKa1a1sTY6KmBHxOxyURsMvXX7QvnRibRyExcJaQYj8e/OnaMFbmuT/7fQi8qMkJIkiSO49zu7Q7Hm6WlYhSBy5fObtn8RnOz0+VyQQg1TeN5fr1NN6CnGRBCqlb19sxUqNcnCh1//H46mZguLa11ArsHgiA0BKha1n/LpWTiesj/5R7PW6He7vvJxD/GYtWyXg+AfTl/b248Ivv2vX9zejxn3LfvFMbYLnJDAFY3FB488FnnwEmZ53mMcUMZ2KOiHvDnxNjOHR90dHZ6vV5FURoFsAzYxwCAE8Hg5wc6fovASCRiu3v1I7IzoJTqui6KYlNT06ZNm9lkRQgxk6qqoig2CiCEyLLc1tbmdrtdLlcgEDAM47lp2hCgXC4HavL5fIIgSJJkT8rXAyCEcM+qvO5O07Rnm20jAFEUMcaKorCZwfO8tyZBECCECCFFUdjv81UALLravFUxxqqq1i+Zd1yTXY8NAR4DZR+hPybKzW4AAAAASUVORK5CYII=" nextheight="860" nextwidth="1286" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Both rollup-boost and op-rbuilder were initially built and are currently maintained by Flashbots. While on the Base side we built and are maintaining websocket proxy and node-reth, as well as made specific changes to rollup-boost and op-rbuilder for our needs. Explained in more detail in the reliability section below.</p><p>Given these changes, how does a transaction submitted to Base now proceed?</p><h1 id="h-lifecycle-of-a-base-mainnet-transaction" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Lifecycle of a Base Mainnet Transaction</h1><p>Let’s trace the journey of a transaction when a user sends an <code>eth_sendRawTransaction</code> to <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://mainnet-base.org"><u>mainnet-base.org</u></a>. This flow is similar before and after Flashblocks, with modification to the block building algorithm. The request undergoes the following stages:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e382f83dc31b9068f947155d229feec6766bad6eeb8bb9ed2edf059e9be5951b.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAALCAIAAACRcxhWAAAACXBIWXMAAAsTAAALEwEAmpwYAAABOUlEQVR4nK1TMZHEMAz0zDG43rX76783AAMwAAEQARMQAREwASMQAiMwAiHwz2cnmfu7NlslO/Gud6WEfTfmnERUSum9773DvdKttVorM7cDa607DVTVzIhIVUWklOLunwZzzjGGmbn7Ra617MBa65t0d2YOIajqnDOlxCc+K+oHxgERgcdai4hAEtGcc+/t7q01qIhIrbWUYmbMrAdaazgeRISZoZtzrrUSEQ6PMVCriCC1qvbe3V0OQAi63x0gboAWET0ej5wzM9dac85EhOB0dIrp9d5ba8/nE0uSUgohuDsRmRlu83q9Yoz1QO/9X0VmdlWkqn4CwUUEiwESCRBXVWOMrbUxBl5VNaVERH/+KAsP68Q76Sd/Tf6dv75EmTHGnxM3/wfYEaxWKQXrcKcBJgwbqO+9fwEcF0jiuotxjwAAAABJRU5ErkJggg==" nextheight="562" nextwidth="1600" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><ol><li><p>The request first reaches our DNS provider for resolution of <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://mainnet-base.org">mainnet-base.org</a>.</p></li><li><p>From there, it is routed to the load balancer of the routing software, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/ethereum-optimism/infra/tree/main/proxyd"><u>Proxyd</u></a></p></li><li><p>Proxyd then routes the request to the private mempool</p></li><li><p>The mempool receives the request and inserts the transaction into its txpool as a pending transaction</p></li><li><p>The mempool maintains P2P connections with the ELs (op-geth, op-rbuilder), ensuring all pending transactions are sync’d in the ELs for block building</p></li><li><p>During each block building loop, the ELs select transactions from their txpool</p></li></ol><h3 id="h-flashblocks-block-building" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Flashblocks Block Building</h3><p>With Flashblocks live, we use op-rbuilder as the block builder. Within op-rbuilder, as explained in the previous blog post, the following factors determine when a transaction is chosen</p><ol><li><p><strong>Transaction fee</strong>: for each 200ms block building loop, transactions are ordered by their fee, same as op-geth</p></li><li><p><strong>Transaction gas limit and remaining gas available</strong>: for each flashblock FB_j, up to j/10 total block gas limit can be used. Therefore, a transaction must have sufficient fee and remain within the available gas for that flashblock</p></li></ol><p>The pseudocode for Flashblock block building that captures the above factors is as follows</p><pre data-type="codeBlock" text="FUNCTION BuildFlashblocks(pending_transactions, total_block_gas_limit):
    flashblocks = []
    block_gas_used = 0
    
    FOR j FROM 0 TO MAX_FLASHBLOCKS_PER_BLOCK:
        NEXT_TIME = MONOTONIC_NOW() + 150ms // next block start time
        current_flashblock_gas_limit = (j / 10) * total_block_gas_limit
        
        // Order pending transactions by fee (descending)
        sorted_transactions = SORT_BY_FEE_DESC(pending_transactions)
        // Select transactions given gas available
        selected_transactions = TOP_TRANSACTIONS_WITHIN_GAS_LIMIT(sorted_transactions, current_flashblock_gas_limit, block_gas_used)

        // Execute transactions
        executed_info, gas_used = EXECUTE_TX(selected_transactions)
        block_gas_used += gas_used
        // Build the flashblock by computing block hash and state root
        flashblock = BUILD_BLOCK(flashblock, executed_info)
        
 // Wait for the remaining amount of time before building the next flashblock
        WAIT_UNTIL(NEXT_TIME)
    END FOR
    
    RETURN flashblocks
END FUNCTION
"><code>FUNCTION BuildFlashblocks(pending_transactions, total_block_gas_limit):
    flashblocks <span class="hljs-operator">=</span> []
    block_gas_used <span class="hljs-operator">=</span> <span class="hljs-number">0</span>
    
    FOR j FROM <span class="hljs-number">0</span> TO MAX_FLASHBLOCKS_PER_BLOCK:
        NEXT_TIME <span class="hljs-operator">=</span> MONOTONIC_NOW() <span class="hljs-operator">+</span> 150ms <span class="hljs-comment">// next block start time</span>
        current_flashblock_gas_limit <span class="hljs-operator">=</span> (j <span class="hljs-operator">/</span> <span class="hljs-number">10</span>) <span class="hljs-operator">*</span> total_block_gas_limit
        
        <span class="hljs-comment">// Order pending transactions by fee (descending)</span>
        sorted_transactions <span class="hljs-operator">=</span> SORT_BY_FEE_DESC(pending_transactions)
        <span class="hljs-comment">// Select transactions given gas available</span>
        selected_transactions <span class="hljs-operator">=</span> TOP_TRANSACTIONS_WITHIN_GAS_LIMIT(sorted_transactions, current_flashblock_gas_limit, block_gas_used)

        <span class="hljs-comment">// Execute transactions</span>
        executed_info, gas_used <span class="hljs-operator">=</span> EXECUTE_TX(selected_transactions)
        block_gas_used <span class="hljs-operator">+</span><span class="hljs-operator">=</span> gas_used
        <span class="hljs-comment">// Build the flashblock by computing block hash and state root</span>
        flashblock <span class="hljs-operator">=</span> BUILD_BLOCK(flashblock, executed_info)
        
 <span class="hljs-comment">// Wait for the remaining amount of time before building the next flashblock</span>
        WAIT_UNTIL(NEXT_TIME)
    END FOR
    
    RETURN flashblocks
END FUNCTION
</code></pre><p>See <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/flashbots/op-rbuilder/blob/main/crates/op-rbuilder/src/builders/flashblocks/payload.rs#L197"><u>here</u></a> for the full implementation in op-rbuilder.</p><h3 id="h-flashblocks-rpc-nodes-node-reth" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Flashblocks RPC Nodes (Node-reth)</h3><p>Once the transaction is chosen and mined, it’s included as part of the flashblock data streamed to websocket proxy, which RPC nodes listen to. The RPC nodes cache the flashblock data. The exact definition of the data stream can be found <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/flashbots/rollup-boost/blob/main/crates/rollup-boost/src/flashblocks/primitives.rs#L63"><u>here</u></a>, we also explain it more thoroughly in our <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.base.org/base-chain/flashblocks/node-providers#interpreting-the-data"><u>documentation page</u></a>.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/50fcb74339e58f28b1fba33912b124032a7745be6a8c3717849e235870048176.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAgCAIAAACO148VAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEr0lEQVR4nLWWQUgbWRjHZy/b5iDRgsomWWq6JrKNhbql9mBciwc96B40u2gPOl63JqA5qIU1l9XCVsu648XIlhEUhSEDdiLUSYo+BPOGWh/B9eWwnYXi9DSFLXPRcUt9i/PWoHG09bA/wkyGvPm///u+730vDPkfYOjNNE0IIToGhIp1hRubm/gIAICqqhhjdAoAgK7rJ0QNw1jPZKh6HkKIqqqXHUUut7vS579Zc6upubnS5+f5mdMjNU1DKFvodGV1dWt7G6EsQlmMczw/I8syxnhRkkSLZHIJQigIAgAAnwJCqKp/nRAlhOi6bhxBCOnovPdUSuYdGYah67qmafRqC7VMjjsFYG1sbDxq8Xj8cUtr68DgYDgc6e3tDYcj8fg0jeYRuZNGcxAqr3d2CmMKoRIOR4IWvb29dXXBltbWQCBQW1tbU/PNyMgInfusjOu6vrW9bRNTVVVpTBHK8vwMQog60jTNyngWQkUQhNm5uWRyaWV1FYC1ZHLJirWynsmoqlooyvMzuq63tH5XWlZ+2VFU4b3W1c1eD1SXf+Hq648ilE2l0oSQrm422HC3rz/65dWKryp9N2tu9fVHCSGSJNmIJq3kAgDi8elFSRobG5+amuL5GZ6fARYQKgAAWrwQQp7nU6k0AGv0kS6UFMQUIUQL0+v1ejyekpISh8NRUlISCFSbdkAINU2jpUJjinHOXpSQg/n5hbq6oK7r56TFqvY3oijWBb/9+90709y3RHGhKMZYVdX5+QUAQCqVhhAuStJZiq93dmbn5gAAgiCIojg/v2Capo2oqqoQKj5flcvtcbndFd5rQ0MPzhK16gTt7e0ahqFpb9YzGWqL2Dptam52Fl8pchaXlpXT2rRF1/XZuTmX2/NDR+ft2jsAgDNF/3z1amV19XbtHWfxlb7+6Homc1ZYqdNUKo0QkmUZQmgvilDWNE2EsgODQ8GGu7IsI4RUVaUpPp53utOtQlKi0QGM8db29lmiiH7HGA/HYhAqgUC1w+FwOp3rmcyz5WVBEDDOJRIJhmFYtmfrjy1BEAYGh6wy37cpKfOo+On81k5F8fj02Nj4L48e/TQ8PDA4yLI9w7HY6OjDeHya4yYhVCBUrFZyiCzL9tt0d3c3/0gI+XlkhGV72tpDXd1sW3vo6+sBn6+qvr6ho/NeW3vo+MgPBwfPlpdPiJrmvmEYoiimUmn6eZ5+vnwYfnqiKLS5qRYY5xDKvlBepFJpWZbzrySTS4Wdn1hhpYdMX3/0qZR8/8972rZpsI66F6Lngn7IW0LIxG/crxMTtMjyUgwhhOO4YLDe769imM+8Xi/DMJ9fuuS28Hg8TqczkUjQWeliAQBW1613u91XKypKy8puVN/w+6v8fj/HTf4nijFOJBIcN8lb5Pt/OBxhWbaxsVGSpPwJijGWZXlo6AHL9kQikVgsRt/6/ckTjuM2NzdPLN8WhBC1eX5zKYDJJ9EWunMKFAvGfDg4KJiVOWfCvb3dUCjkcrnyW+ViTgug02683GAsQqHvLxQB5pzfIIRNTU0/3r8/NTX1iXLko6K6/laSpNHRhx89BS4ginFOFEVBECCEFzF6rmjBX7BPF/0XaRyM6iuvB2QAAAAASUVORK5CYII=" nextheight="848" nextwidth="737" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>When a flashblocks supported RPC method is called, it retrieves the data from the cache and returns it if available. (E.g. <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/node-reth/blob/main/crates/flashblocks-rpc/src/rpc.rs#L145"><u>eth_getTransactionReceipt</u></a>)</p><h1 id="h-reliability-and-performance-improvements" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Reliability and Performance Improvements</h1><p>During the development of Flashblocks, we made several improvements to ensure the system is highly available and Flashblocks could consistently be produced in 200ms.</p><h3 id="h-enabling-builder-failover" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Enabling Builder Failover</h3><p>With the integration of op-rbuilder, it became necessary to incorporate builder health into the op-conductor’s health check. We enabled <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/ethereum-optimism/optimism/blob/0148766dae790df6d1827c24e7d66013e952a39a/op-conductor/conductor/service.go#L217"><u>healthcheck to rollup-boost</u></a> which verifies if the builder remains sync’d within the tip of the chain. If the builder falls behind, the op-conductor will initiate a leadership transfer.</p><p>This significantly enhances Flashblocks’ availability as Flashblocks will not experience a long period of outage due to unhealthy builders. However, the downside is that if all builders are down, the op-conductor will be unable to elect a healthy leader, leading to a chain halt. Nevertheless, the probability of all builders being down simultaneously is rare, making this a reasonable compromise.&nbsp;</p><h3 id="h-optimizing-state-root-calculation" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Optimizing State Root Calculation</h3><p>In the initial design, the state root was calculated for each flashblock. This was due to the complexity of determining which flashblock would ultimately become the finalized block that the builder needed to return to the op-node. Without a state root, the op-node cannot promote the block as finalized.</p><p>While this approach functioned correctly for Base Sepolia, the high transaction volume on Base Mainnet made each state root calculation take approximately 130ms on average. This pushed the total block building time close to 200ms, making it hard to build more than 9 flashblocks, potentially reducing throughput.</p><p>In order to fix this, instead of having the builder compute the state root for every flashblock, we modified rollup-boost to take the transactions from the builder, then used an additional FCU to use op-geth to build a block with state root. (Many thanks to Ferran from Flashbots for this suggestion!)</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b9e6143c5e46e6cb70d50454a588643925ffce0a3ad5e80b90b5e16cd6a807e7.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAARCAIAAAAzPjmrAAAACXBIWXMAAAsTAAALEwEAmpwYAAAD9klEQVR4nLWUT2zbZBjGDVs5ZmxoDA2hio0DRyq0QtbLJiZSVoqQssF8mUGQSE21oFhrmx3miCi0g5JJMxxaiki3JeuYVml1USFCaSOa1lOnWHU319nqOBFOW+E0f+x40pdVyodiN1lb9QBI/A4+vPr8PO/7fJ9eZP3JOs8n06mMqmo8nxRFCeqAKmUIYTqVSacy2Ww+m83zfNIoLi//xfPJelErPVZVjWUT9xcesuxiJBJbXHwoSRlJyiCKol72//SW+X0E2XOy7ZNgkKqpV4GwMnJj/JXGJgTZbzI1dnd/rRtUolN3j7Z8aDI1IkiD0+nNZvMsm3A6vc/veXXvvkPnu/ri9x4YjSIAgLGxCOHxf25zXfb/GIvF4VZisTiOf3kWc2DYuWCQMgxYNkF4/Bh27vTpz4Z+uKmqmihKgcDt+jGeTxqNIoZKbi13bfi6oijFYkGSJLfbbbVaOzs7ZVkuFgslVUmJaUO65luBsJLPFYzE6kWtpBkVffpNBoIg7Hp2Vzgcjkaj4XCYJEmfz0eSZDQanZmdvTN259rwdQihoigcx9E0LQiC3lZeUVRDYf3Jun5ArVc2IqpdaVkrPd4WTv22IYQHDrxEkuTEbxMWi6WpqcntvrC09GhwcEiWZeN345iiqFpJ28FAK2kry6ubRTcDIRwcHBof/6W9vd2tg2FYIBDo673EMMxmufoE2yMCAGSz+R17B/o3Nk2z82w0GvX5fIROKBRCkGdQFKVpmqIow+lfGxhACA8ffm1gYIBhGAzDrFar3W5HUfQLp+vEOycsOjiOMwzj/9afEtP/xeDke22hUEjQkWWZ4zgMO2s2txAEASGUZVkQBI7jbo78vLK88k8NNgdlNrdcHb4qCALDMBRFYRiG4/ipUx/ZbDaCIGiaZhiG47jR0dEdDAAoq6qWW6saKIoCtqLomU7/EZu7Oyfp0PQMjuP9/f1d53s8Hg9JksZkAIDW1la73V43ePqKIISR3yPh8MTx48dQFG1ubrbZbCiKWiyWM2c+npqKYBi2tPRo23C5tXxdyEAQBEna2GYbBgCUWTYhitLERCQSmb6/wBeLBaVGsViYn+dYNnHkSMtkZFoUJUMxncqwbMJYjnNzCwCU87kCyybSqYwoSiybMAKvRqQo6qW+gTfefBdB9re1fRoI3N6afiUYpF548fW9+w491/Cy0+lVVQ3CyuQk/fbRDxp2H0QQpKODWF2R4/cedHQQJlNjw+6DTqd3dqb6aqsGAIBxarL3q+8cju4rVwL1ZVd7P5VYLE4Q39jtuMt18datX42NxPPJ3t7ve3q8DkdPMEipqvantDocGHW5LnZ1e0dujBtr/+kr+v/4GxvqHeTU/D/1AAAAAElFTkSuQmCC" nextheight="830" nextwidth="1600" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>As a result of this change, the P50 Flashblock building time dramatically decreased from 150ms to a mere 10ms, removing this issue as a launch blocker.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/1a2349bacdd0f57af54edde558f998b422ff3b31e5842b3d6794e3e14089ead4.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAPCAIAAAAK4lpAAAAACXBIWXMAAAsTAAALEwEAmpwYAAACxElEQVR4nJ1Tb0gTcRjexyIpqC8a9EX6WhElSIKkQfWhJlgIy8KaIjlTWqbbEpvrcJ7ur7Q8Xbj5oZrLdF3lnJT4ZSAZjhCVwDSylie6+92l225wtze2k1GW4Xx4P7z3u/u97/M8772SgYFBfSuubW62Wjus1g7cYDCZzY86iUSOt7lcfSq1xu/3A0A0fayHwxKlsk6SxK49Gfv2HxCT3Rl7xcMLF6USiUSl1gAATdPpNkCIkQDAk6cusQTLsAgxLMOyDPv7d/ROkWhA0zQA8IIASYgVUzn8eZIuotFoQoHJbCm7If+PyzGOm5v/EpiaBYinbVHqYSsWfFIc6RtrszmTJLj0FESjUZerr0Wv30rBejgMEO95NlitagGII8Skp6DH4Xw7OhoIBER2YvCCwAuCmIcjEQCwOfquVmt2oqDH4fR6hz9MTv7rNZeyiHA+lynUABCimRSPbTUwmS0msxk3GL4FKZ2RuFn/oJ8c9r8PBKZmV0MhzNxdpzWEaFpe23Sp/E5qyADx7Qw8YdH0zEwkEp6b/9qof/h6ZGxhMag1dN7VGnVGQl7bpGjAcJujqgErVajPX1GYOntvN7XJqlSmzl7c5hDd+xspezcWzePxVCluAUCM42Icl2L3PfiDF3he4MU5uz3efnJYZyRaLN2roZDOSIg/rnhrU6SKbCxaWqsUTbqPdzwmfWO8IKC19U2xsBic+Dgz/enzxh7EOM7lcmMYjmGtbvcASQ6R5JDmnra93eLzvXvzagTD8Joapd3ueNH/kiS99fWNXV32YycKsw6fPFtUmpl9NLdAKi2pkJZUHM87l5l9RHa9pqxSqb7fmmiAECMqkJdXSouKx8cnlpYoilo5lZcvL69EiEWILb5cknXwkMVqQ4ihqBVZ6bX802d+smsxjvN4PAUFhfZuu+gMQRA5ObnLFMUyzDK1jBDzC2jFuHDnPAl8AAAAAElFTkSuQmCC" nextheight="544" nextwidth="1138" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><br><h3 id="h-resolving-the-transaction-pool-discrepancy-between-geth-and-reth" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Resolving the Transaction Pool discrepancy between Geth and Reth</h3><p>Moving our builder from op-geth (Geth based) to op-rbuilder (Reth based) meant shifting from using Geth’s transaction pool implementation to Reth's transaction pool implementation.</p><p>Soon after our launch, we observed some unusual behavior: the Coinbase retail staking team's transactions stopped getting included, causing a large backlog of transactions.</p><p>We then thoroughly examined the differences between the Geth and Reth transaction pool implementations and identified the following distinctions:</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/ethereum/go-ethereum/blob/master/core/txpool/txpool.go#L37"><u>Geth</u></a></p><ul><li><p><strong>Pending</strong>: Next-nonce transactions with no nonce-gap.</p></li><li><p><strong>Queued</strong>: Transactions with nonce-gaps.</p></li></ul><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/paradigmxyz/reth/blob/main/crates/transaction-pool/src/pool/txpool.rs#L87"><u>Reth</u></a></p><ul><li><p><strong>Pending</strong>: Next-nonce transactions with a fee higher than the base fee.</p></li><li><p><strong>Queued</strong>:</p><ul><li><p>Basefee subpool: Transactions below the base fee, even if they are next-nonce.</p></li><li><p>Transactions with a nonce-gap.</p></li></ul></li></ul><p>Consequently, if a transaction is a next-nonce transaction but has a fee lower than the base fee, it would remain pending for Geth but be queued for Reth. Once the transaction pool lifetime value is reached, queued transactions are dropped, and Geth would not rebroadcast these transactions as it always assumes pending transactions are successfully P2P'd.</p><p>To address this, we built the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/infra/tree/master/crates/mempool-rebroadcaster"><u>Mempool-rebroadcaster</u></a>. This tool checks for discrepancies between the Geth and Reth txpools and rebroadcasts transactions between them. This ensures that even if a transaction is dropped from Reth, it continues to be picked up again.</p><h3 id="h-reducing-reorg-rates" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Reducing Reorg rates</h3><p>In our initial implementation, builders sent flashblocks directly to the websocket proxy. Because the builder was unaware of the actual time allocated by the op-node for block building, this could lead to tail flashblocks reorgs: the tail flashblocks were streamed out but not included in the actual block.</p><p>Flashbots team added a new mechanism to instead of letting the builder to stream to websocket proxy, the builder streams to rollup-boost then rollup-boost streams to websocket proxy. It works as follows</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/6ea3241ab63b67444f6c0753077d0a8f2b76156eb732a1b535018e5c9704adfb.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAXCAIAAADlZ9q2AAAACXBIWXMAAAsTAAALEwEAmpwYAAAFP0lEQVR4nH1Vf0wTZxg+ly1xuGTJDBjnnMswiz+CJFSGSjHbEYMzpW4VRRwdcTVG6kRx1j+aocUJ62IRodUwMJ6mLdU2iFW2WpzgkuLM6pjt+DXaacvWs1LvWu9r9e4ae8v1tlutxSeXL/fj/b7n3vd9vu+BKIr2evxTfhTDwig67fX4CQIwDEOSJMMiwQUEgyEcj2BY2Ovx//eJjeHuuZEggNfjx7Awjkdc7vGr9ht3XCMQRdEndcZtn9blzHuvuFis1emn/AGGSXCTSZIkCNDaenbhovw3FyxfuCj/yJGTGBZmMsHnCzQ0tC1dVpyVtaCwsMzU3Ren4yzB6TM98t0NMPzJjh2K02d60uZTFN3VaS5bv7WkpLxs/daWFgTHIxkJpvyoWt25YcO2NWs+qqmpN5ttDMNADMNEQZQkSQwLUxSNY+GOjg6LxYIkodVqEQThioZh4Vj0MUEAcmYQBIiCKIaF43ScmwUly5dg7yBIoznmcDhqa2tlMll5uVgkEgmFQpFIZLPZzGYzgiBcVzL+fmrGwWCIK/K/GXAYHRkjCIJvYCrIJHbLvxgYGHz0KKLRaGAYVigUWq1WKpUWFRUJhcLc3Nzm5uZQKDQwMDj86zA/938CHI9QFM1Lor3tu+rqnTgWxjE8CqJxmp43b77F0oMgyKpVqzeKN+blrYDhUolkU9+Vvmv2a/32/qtX7U7n7e5uk+37/kwEyR7wj70XrzQ2HmWYRJymk2VJ4Mnm3717V6fTGQz65uYmjeaY0WhMSzdOx5MlSiHg2vDwIcaLmhd4Kny+AAAxggCqxia1WlO390u5fE9t7S6pVKpQKJRKpdVqJUly8o/J1uNtfCUyZsBuLs/k8AWTSn/ukNl09IKpceDaKdn2D7ZsLvxKuVkszquqKhKLlx5U7GhpOdHe3tbR0cGpzul0qlSqOa++loEARe/zBOz//vnTpk1zXoGgBdnQ6y9Bb89nx5chSLASmg1Bc7OgzZVZE+OX+fgXlYhDLPo4TscZJjE5MTE+Pn5j8MbNIcfY2MjY2EgUgGQPcIIgouBRFACSfBIKTYdCIYIAaReGhe/d+5vT6zME2dk5XI4ikQiG4YKCAoFAIBQKKysrnU6n1WpFkHOpHUpVRCq43ZqBoL5+/9DQkEqlkslkdXV1CoVCIpHU1GyXy/fsqt3jdo/2Xux7dtHEiwnSS2S397tcbofDYUkCQRCDQX/wwGcHD1Q3KGV6/bcQBF04f3zYeXnwuuGXW72RCE5RdKrkuHHGDILBUFrWT59SOm354neg/HzorWyIw9zZ0JIl0KHD70ejDzLmwR4V9x9kIOBk+uzZ9eSvqQmvx+3zjXk9d+78dtPrcev1Xa2tTSdam463tHDStFgsNpvNarXabLZAIECSZOpxC/FU/EbjaJ4v7nmTpafn0r59+8vLP66qqhYKhWVlZTAMSyQSmUwmTcJoNNbvq58FzeL8h8+ATTN4/8EMVpXgLvU36t/dLoZhbLYfZLLPlUolv4c5RbhcLoZJuFwuk8nEL8sSoOg0hoWDwRDnms/rD0WnAYjNfSPHbv9xYsLLaYGHKwmn0zniHnG7R93u0Vs/36YoGkWnWQIAYl2d5urqvQsX5a1bt+WkzjjlR1NXByDWfuJc7rsFAgG8bFnx0a9PzeRoPl/g8KH2wsJ12TmL167deBbpBSDGWqapu0++u2HV6vVVVXKD4fLzlnn6TE9pacWKFSUla1nTBiCWkQBFp9Xqzg9LKwQrSysqdl66dJ31ZM4L+TkZDT31ZWpwGgCIpUUyDPMPZ8TTELdQ23AAAAAASUVORK5CYII=" nextheight="1146" nextwidth="1600" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><ul><li><p>Op-node sends FCU to rollup-boost, rollup-boost proxies FCU to op-rbuilder to start block building</p></li><li><p>In each block building loop, op-rbuilder streams flashblock to rollup-boost, rollup-boost stores the flashblocks in a variable named <code>best_payload</code></p></li><li><p>At the end of the block building, op-node sends get_payload to rollup-boost, rollup-boost returns <code>best_payload</code>, and clears <code>best_payload</code></p></li><li><p>If op-rbuilder continues to send flashblock with the same <code>payload_id</code>, it gets rejected</p></li></ul><p>This significantly improved our reorg rate, reducing it from 0.2% to effectively 0:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/d0190b04f1217502332cfc97dbf57907c01fd857f54e85d711a49c47d18019f1.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAGCAIAAAAt7QuIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABaklEQVR4nGO4devWs2fPPuMFz5+/+EwuYLh+/frjJ0+Qhb59/Xrz1t1V6zf/+/f3/YcP//79beuedOXadfJsYrh9+86nTx//////+fPn339+//v39////9dv3Morqf737y/Estzi2pu37obGpiVlFpNswadPH1NzSisbOjZs3tHeO7W8rm3+4lWTps8NiEo5f/nah4+fyuva9K3cTp29wCKiwMDAMGfR8v+kAFAQ8YgryuuYMzBwMnBJSCpp65nZWTl4xiRm6pra+ofGWjl4KmkZMzDwiMprmtu5p2blX7p4acuWbYcPH9m+fcfhw0fWrl1/+uzZbdu2nT59GiICJ8+fv8CwftM2dU3t0JCQpJTU06dP79y5c9+BA/sOHDh79szmLVuWLl8+d96C1avXrFq1dvXqNYcPH5kzd97169fnzJ13+uzZ5StWnD59urOz8/r167PnzDl9+jSEnD9/wfXr15evWPHixSsAJYS9pymls7wAAAAASUVORK5CYII=" nextheight="322" nextwidth="1600" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><h1 id="h-future-work" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Future Work</h1><h3 id="h-rpc-improvements" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">RPC Improvements</h3><p>We’re currently adding new Flashblocks-aware RPCs: <code>eth_call</code> and <code>eth_estimateGas</code>. Our implementation involves having the node copies over the latest state, replays the Flashblock streams on top, and executes the request against that overlay. This makes simulations and gas estimates reflect preconfirmation state. The feature is in active development and will land after validation and testing.</p><h3 id="h-websocket" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Websocket</h3><p>Websocket is the low-latency backbone for traders and for teams that host their own RPC nodes. We aim to operate the public websocket with high availability and backpressure controls. However, it’s worth noting that applications should try to avoid having dependency on websocket as RPCs provide stable RPC behavior and automatic failover to regular blocks whenever Flashblocks are down.</p><h3 id="h-other-block-building-updates" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Other block building updates</h3><p>Enabling the custom builder (op-rbuilder) as part of the Flashblocks launch has paved the way for more block building innovation, with the potential to unlock more blockspace capacity and efficiency for Base users, and even provide unique capabilities for developers. To stay up to date on any planned block building changes, follow our <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.base.org/base-chain/network-information/block-building"><u>docs</u></a> and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://status.base.org/"><u>status page</u></a>.</p><h1 id="h-get-involved" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Get Involved</h1><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/node/tree/main/reth#running-a-reth-node">Running a flashblock-aware RPC node</a></p></li><li><p>Report problems:<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/node-reth/issues"> Open a GitHub issue</a></p></li><li><p>Questions / feedback: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://discord.com/invite/buildonbase?utm_source=dotorg&amp;utm_medium=nav">Join the Discord</a></p></li></ul><hr><p>If you’re interested in helping us build a global economy that increases innovation, creativity, and freedom, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.base.org/jobs">we’re hiring</a> and would love to hear from you.</p><p>Follow us on social to stay up to date with the latest: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/base">X</a> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/i/lists/1876354838289932418">Base team on X</a>) | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://warpcast.com/base/">Farcaster</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://discord.com/invite/buildonbase?utm_source=dotorg&amp;utm_medium=nav">Discord</a></p>]]></content:encoded>
            <author>base-engineering-blog@newsletter.paragraph.com (Cody Wang)</author>
            <category>infrastructure</category>
            <category>flashblocks</category>
            <category>l2 protocol</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/0261b86fdbafb5e9f0fef2fd17409d802978996662c78f391be90a6ab97f1fd8.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Benchmarking ZKP Systems for Passkey ECDSA Verification]]></title>
            <link>https://blog.base.dev/benchmarking-zkp-systems</link>
            <guid>gHUvTluLQFAHmP76hmDZ</guid>
            <pubDate>Tue, 02 Sep 2025 15:25:08 GMT</pubDate>
            <description><![CDATA[In this post, the Base team dives into our recent benchmarksing of 4 major ZKP systems: SnarkJS, Rapidsnark, Gnark, and Noir.]]></description>
            <content:encoded><![CDATA[<p>The Base team is exploring various ways to enable onchain privacy for smart wallet passkey-based accounts, and in the spirit of building in the open, we're sharing our recent deep dive into four leading ZKP systems: SnarkJS, Rapidsnark, Gnark, and Noir. We benchmarked their performance on verifying a passkey signature and focused on two metrics:</p><ul><li><p><strong>Proof generation time</strong>: Time it took to create the ZKP on various hardware configurations.</p></li><li><p><strong>Gas cost</strong>: The gas required on an EVM to verify the proof in a smart contract.</p></li></ul><p>Full details on the benchmarking setup, including scripts and configurations, are available in our<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/lukasrosario/zk-snark-ecdsa-benchmarks"><u> GitHub repository</u></a>.</p><hr><h2 id="h-what-are-zero-knowledge-proofs-zkps" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>What Are Zero-Knowledge Proofs (ZKPs)?</strong></h2><p>Zero-knowledge proofs let you prove you know something without revealing what you know. They work by constructing mathematical proofs that demonstrate you performed a computation correctly, without revealing the secret inputs you used. Think of it like proving you're over 18 without showing your exact age, or proving you have enough money in your account without revealing your balance.</p><p>In crypto, ZKPs are making an impact on both privacy and scaling related use cases.&nbsp;</p><p>For privacy, projects like Zcash or Iron Fish use ZKPs to enable fully private transactions where amounts, asset types, and transaction participants are hidden, yet the network can still verify transactions are valid.&nbsp;</p><p>For scaling, zkRollups use ZKPs to process thousands of transactions offchain, then submit a single compact proof that mathematically guarantees all those transactions are valid, saving on both storage and compute cost. General-purpose zkVMs like Risc0 and SP1 enable proving arbitrary program execution, opening up use cases beyond just transaction processing.</p><hr><h2 id="h-why-benchmark-passkey-verification-in-zero-knowledge" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Why Benchmark Passkey Verification in Zero-Knowledge?&nbsp;</strong></h2><p>Passkeys are the modern passwordless authentication system. You might have noticed a number of major platforms such as Google, GitHub, PayPal, and other popular websites you use today offer passkey logins as opposed to just using your password.&nbsp;</p><p>Passkey based smart wallets work similarly. Unlike traditional seed phrases or private keys that users must memorize or store securely, passkeys are securely stored and managed by your device's operating system. In many implementations they can be accessed via biometric data like Face ID or fingerprint offering better UX, and can be automatically synced across your devices via services like iCloud, 1Password, or Google Password Manager.</p><p>Base Accounts are passkey-based smart wallets, relying on the sec256r1 (P-256) curve for ECDSA signature generation, the same standard used by Apple, Google, and the FIDO2 WebAuthn specification. ​​Verifying a passkey signature within a zero-knowledge proof enables powerful privacy features. Users can prove ownership of a public key by proving validity of a signature without directly revealing their public key enabling:</p><ul><li><p>Anonymous group membership proofs for DAOs</p></li><li><p>Private authentication for sensitive applications</p></li><li><p>Delegating proof generation to relayers while maintaining privacy</p></li></ul><p>Please note that in practice such proofs would have two steps: one step to prove ownership over a key (e.g. validity of a signature within a ZKP), and the second step would be to prove that the public key (or even a commitment/hash of that public key) is within a certain set, such as a merkle tree, onchain. For these benchmarks we just focused on the first step of verifying a passkey signature within a ZKP to validate the idea.&nbsp;</p><p>Although these are just explorations for now, it’s a topic that the Base Privacy pod (acquired team from Iron Fish) is really passionate about.&nbsp;</p><p>Generating zero-knowledge proofs for passkey signatures is also a fun technical problem. Passkeys use the P-256 elliptic curve for signatures, but most efficient and popular ZKP systems for EVMs use the BN254 curve that has a different mathematical field. This creates a "curve mismatch" problem. Think of it like trying to do math where your calculator only works with numbers 1-100, but you need to work with numbers 1-1000. You need to break down the larger numbers into smaller pieces and emulate the math you want to do. P-256 curve elements can't be directly represented as single values in BN254-based ZKP systems. Instead, they must be "emulated" through complex mathematical operations, resulting in large circuits with around 2 million constraints. For comparison, the most complex ZKP circuit in both Iron Fish and Zcash is around 100k constraints.&nbsp;</p><hr><h2 id="h-methodology" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Methodology</strong></h2><p>We tested four leading ZKP frameworks, each with different strengths:</p><ul><li><p>SnarkJS: JavaScript-based, runs in browsers, great for development</p></li><li><p>Rapidsnark: C++ optimized version of SnarkJS for production speed</p></li><li><p>Gnark: Go-based system with strong security features</p></li><li><p>Noir: Rust-based with specialized optimizations for complex operations<br></p></li></ul><p>All of these frameworks rely on circuit engines, which convert high-level code into the low-level mathematical constraints that ZKP systems understand. Each framework has its own way of expressing and optimizing these constraints.</p><p>Some ZKP systems have what’s called a “trusted setup” that is unique per every circuit and must be generated as part of the setup phase. The more complicated a circuit/program is, the more computationally intensive the trusted setup step is. Noir uses a ZKP system that relies on a “universal trusted setup” instead, which is generated just once and reused for many circuits. Not having to regenerate the trusted setup was a huge developer experience benefit.&nbsp;</p><p><strong>Key Technical Choices:</strong></p><ul><li><p>BN254 curve vs BLS12-381: We chose BN254 for better performance and lower gas costs in EVM environments</p></li><li><p>P-256 Implementation: Since building P-256 field arithmetic from scratch was outside our scope, we used existing optimized implementations (called "gadgets") where available</p></li><li><p>For Noir specifically, we leveraged their "blackbox" functionality which are specialized operations handled directly by the underlying Barretenberg proving system in optimized C++ code, rather than being expressed as individual ZKP constraints.</p></li></ul><br><table style="min-width: 743px"><colgroup><col style="width: 161px"><col style="width: 153px"><col style="width: 135px"><col style="width: 141px"><col style="width: 128px"><col></colgroup><tbody><tr><th colspan="1" rowspan="1" colwidth="161"><p><strong>ZKP stack</strong></p></th><th colspan="1" rowspan="1" colwidth="153"><p><strong>Browser support?&nbsp;</strong></p></th><th colspan="1" rowspan="1" colwidth="135"><p><strong>Language</strong></p></th><th colspan="1" rowspan="1" colwidth="141"><p><strong>Strengths</strong></p></th><th colspan="1" rowspan="1" colwidth="128"><p><strong>Proving system &amp; curve</strong></p></th><th colspan="1" rowspan="1"><p><strong>Setup Requirements</strong></p></th></tr><tr><td colspan="1" rowspan="1" colwidth="161"><p><strong>Proof generation:</strong></p><p><strong> </strong><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/iden3/snarkjs"><strong><u>SnarkJS</u></strong></a><strong>&nbsp;</strong></p><p><br></p><p>Circuit engine:</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.circom.io/"><u>circom</u></a> + <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/privacy-scaling-explorations/circom-ecdsa-p256"><u>p256 gadget</u></a></p></td><td colspan="1" rowspan="1" colwidth="153"><p>Yes <span data-name="check_mark_button" class="emoji" data-type="emoji">✅</span></p></td><td colspan="1" rowspan="1" colwidth="135"><p>JavaScript</p></td><td colspan="1" rowspan="1" colwidth="141"><p>Versatile, browser compatible, but slower on large circuits</p><p><br></p></td><td colspan="1" rowspan="1" colwidth="128"><p>Groth16<br>BN254 curve</p></td><td colspan="1" rowspan="1"><p>Trusted setup&nbsp;</p></td></tr><tr><td colspan="1" rowspan="1" colwidth="161"><p><strong>Proof generation:</strong><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/iden3/rapidsnark"><strong><u>Rapidsnark</u></strong></a></p><p><br>Circuit engine:</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.circom.io/"><u>circom</u></a> + <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/privacy-scaling-explorations/circom-ecdsa-p256"><u>p256 gadget</u></a></p></td><td colspan="1" rowspan="1" colwidth="153"><p>No <span data-name="cross_mark" class="emoji" data-type="emoji">❌</span></p></td><td colspan="1" rowspan="1" colwidth="135"><p>C++</p></td><td colspan="1" rowspan="1" colwidth="141"><p>Optimized for speed</p><p><br></p></td><td colspan="1" rowspan="1" colwidth="128"><p>Groth16<br>BN254 curve</p></td><td colspan="1" rowspan="1"><p>Trusted setup&nbsp;</p></td></tr><tr><td colspan="1" rowspan="1" colwidth="161"><p><strong>Proof generation</strong></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/Consensys/gnark"><u>Gnark</u></a></p><p><br></p><p>Circuit engine: gnark</p></td><td colspan="1" rowspan="1" colwidth="153"><p>No <span data-name="cross_mark" class="emoji" data-type="emoji">❌</span></p></td><td colspan="1" rowspan="1" colwidth="135"><p>Go</p></td><td colspan="1" rowspan="1" colwidth="141"><p>Strong security features and fast proving speeds</p></td><td colspan="1" rowspan="1" colwidth="128"><p>Groth16<br>BN254 curve</p></td><td colspan="1" rowspan="1"><p>Trusted setup&nbsp;</p></td></tr><tr><td colspan="1" rowspan="1" colwidth="161"><p><strong>Circuit engine:</strong></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/noir-lang"><strong><u>Noir</u></strong></a> using <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://noir-lang.org/docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification#ecdsa_secp256r1verify_signature-1"><u>P256 ECDSA signature</u></a> blackbox&nbsp;</p><p><br></p><p>Proof generation: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/AztecProtocol/barretenberg"><u>BB</u></a> (barretenberg)</p></td><td colspan="1" rowspan="1" colwidth="153"><p>Yes <span data-name="check_mark_button" class="emoji" data-type="emoji">✅</span>, but WASM support is 3.5-4x slower than native&nbsp;</p><br><p><span data-name="information_source" class="emoji" data-type="emoji">ℹ</span> Benchmarks were done using native Noir</p></td><td colspan="1" rowspan="1" colwidth="135"><p>Rust</p></td><td colspan="1" rowspan="1" colwidth="141"><p>Efficient for custom ops with blackboxes</p></td><td colspan="1" rowspan="1" colwidth="128"><p><br></p><p>UltraHonk</p><p>BN254 curve</p><p><br></p></td><td colspan="1" rowspan="1"><p>Universal trusted setup</p><br></td></tr></tbody></table><br><hr><h2 id="h-benchmark-setup" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Benchmark Setup</strong></h2><p>We ran the benchmarks on AWS EC2 instances to simulate real-world cloud environments:</p><ul><li><p><strong>t4g.medium</strong>: ARM Graviton2, 2 vCPUs, 4GB RAM—Cost-effective ARM</p></li><li><p><strong>c7g.xlarge</strong>: ARM Graviton3, 4 vCPUs, 8GB RAM</p></li><li><p><strong>c7i.2xlarge:</strong> Intel, 8 vCPUs, 16GB RAM</p></li><li><p><strong>c7i.4xlarge:</strong> Intel, 16 vCPUs, 32GB RAM</p></li><li><p><strong>c7i.8xlarge:</strong> Intel, 32 vCPUs, 64GB RAM&nbsp;</p></li></ul><p>Each benchmark involved generating proofs for the ECDSA signature verification circuit multiple times to capture minimum, average, and maximum values, along with standard deviations. Gas costs were measured for on-chain verification in an EVM-compatible smart contract.</p><p>The raw data and plots are available in our<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/lukasrosario/zk-snark-ecdsa-benchmarks/tree/main/ec2-benchmarks/collected_results_20250708_190933"> <u>results directory</u></a>.</p><hr><h2 id="h-results" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Results</strong></h2><h3 id="h-proof-generation-times" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Proof Generation Times</strong></h3><p>Noir dominated in speed, consistently achieving 5-50x faster proof generation than Groth16-based systems:</p><ul><li><p>c7i.2xlarge (8 vCPU, 16GB RAM): Noir ~2.1s, others 40-50s</p></li><li><p>c7i.4xlarge (16 vCPU, 32GB RAM): Noir ~1.1s, others 20-30s</p></li><li><p>c7i.8xlarge (32 vCPU, 64GB RAM): Noir ~0.6s, others 15-25s</p></li></ul><p>Among Groth16 systems, Gnark performed best, followed by Rapidsnark and SnarkJS.</p><p>Below are summarized benchmark runs (all times in seconds):</p><h3 id="h-t4gmedium-2-vcpus-4gb" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>t4g.medium (2 vCPUs, 4GB)</strong></h3><p><em>Couldn’t run SnarkJS and Rapidsnark due to memory overhead of generating the trusted setup</em></p><figure float="none" width="766px" data-type="figure" class="img-center" style="max-width: 766px;"><img src="https://storage.googleapis.com/papyrus_images/b82efe8f1d63fbaa790d4baf3b111a1b.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAATCAIAAAB+9pigAAAACXBIWXMAAAsTAAALEwEAmpwYAAADOUlEQVR4nLWVXWgUVxTH/6gYoS9WrSLVamLs6kNqjYq2+FJQH1o/s0kQbVIblVLFB5EgEaU1IMUHnwRF+5CH+vnix0OCqaSEmJWQdbM0bFMIYz417l531s3OzujsmXuvzIxZ19kFo+Kf8zBz51x+93/OGS6klKZpapqmvynDMBhjbW1tzc3NgUCgu7s7EAj09vaGHoQ6OzofKg8Ftzhl7OA8I0RGCNOydF2XjrgjKSUMwxgZGckH6LquaVrCUTKZZIy5z8OPh3v+7Rl9Mvr/wKOW9q728H+Rru72CxfvnjundHQwxiyy+mLKnX/+DveEiQhEpKoqEbnkLD/3VUoppJBS9o314dCn+Bn4ZTrW70BZLb496MO8OmAbcHJq8XgqJaW4dPnKjZu3WlvvappmA8bGxoiI58jzyp0VKWVwIIgdsGMbUPYdSqvx1Z5izNoLbAV+KypJJpNElH4SS8bjT+NxGyClnIwD7qyEh8OoBvxAJbByI5btwsq6Jfhsn0NsLCpJ64aup5vONpz/49cLpw+0XD9j9yDfQb7IOUFoMPQasGIDlu7E13VLMGcCYJfIMIxwV1uwsyV0v3VU6YGmaaqqmqY5mRKFsgB/LmD23okeuCXKtY5IJBKNRj01eXuJ/AUAjUXFmqYRkbCPZHFuCc6hKEoikXjnHvgLO3ABbzhQFIUx9s4OKgv2oKQwIL8Hb2+yf9I90HWdMfYRAUQUjUbdsSFOnimyiKyJT+8J4Jwzxjyr+aIPd2AYRozFiFPKeK4MDKrJ8fFoTAkGB8NhQ028cjAUwk6gCjamfBN8u1C+rxTz9gPbgZOflI6nUl5AhjJkUNO9JntPNbB7Clb/iOXVKN+zDqgBNgN/ba9ws4NDQfuom4EtwJfr8UUFltV8jmm1wA/AccwvMEXEKf0sfbXz2uwjvpKGsgWHV6GqHhVHUdVQ61vze9Giw5h7+0h9RgjrRSYyFCk7sdZ3bNXyY2tm/nQIlQ2oaaz65vtTMxbXY+GfWyufxuNegHvheAY/ob7KE06YpuleBtn7xNXj0ZFU6lk2LTtsXoArIcVEvN4gHLCu6/39/e4vmZtmmhmL2/eEcDLdZA/gJTNQMqWJdWZzAAAAAElFTkSuQmCC" nextheight="927" nextwidth="1600" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-c7gxlarge-4-vcpus-8gb-ram" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>c7g.xlarge (4 vCPUs, 8GB RAM)</strong></h3><p><em>Couldn’t run SnarkJS and Rapidsnark due to memory overhead of generating the trusted setup</em></p><figure float="none" width="767px" data-type="figure" class="img-center" style="max-width: 767px;"><img src="https://storage.googleapis.com/papyrus_images/276c6f6881dd241174337fc07fe96bad.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAATCAIAAAB+9pigAAAACXBIWXMAAAsTAAALEwEAmpwYAAADHElEQVR4nLWVW0gUYRTH/1BhTxEaWHTBzG7QVZEIsgjKyKKblyytoLQI6iWIVLLIqJ7Eiigyox4qKOhCD5ubFLFtF0zdHixIVrM0d113p9HZkWbO980XM6Pbtu2D3f6cl+9whv/5nfMNH4QQmqYpiqL+IlmW3W630+l0uVwvLL1rfdf+ob3pVZOvyyeEMLhuhjB0wwxiTFVVYYlbEkKAiLq7u+MaqKoqWZJlORQKyZLs6/U5m5y192sdjQ5n49vLd+qvO9yvGp67ai87zp3zPnsWCAQYsfe93vqnjz0tHiKCECIUChGR7Rzxjz6K4UxLVwuKgSJgG7A4G3OKkFGaisQ9wEbgxOiU/oEBIYwbN2/du//A6WxQFMUk8Pv9RMSjFHPkVkYI0fyxGXnAZmALsHAVZhZi0e5UJO0BNgBVCamyLBNR2NcrB4N9waBpIISQJGmEBJ5PHhQAuTBthg1mYEKJRVCVkBpWB1U1fO1C+cXT+y6d2V9/uxqc85GPyBMxyI02SBoa0ajpSjg8ODjoef2k0eVocju+tHsgSdLvjaggliAyouPDI4ruDF6v99fsnxMoChEZZkuMc2Zwjo6ODr/fr2laTMsjJ5gRtYM4BPZP8G8JRLRBXIJ/uQMi+r8EQoienh6ddOJEnHSm68SGQtMYkR3EfybIHTGBECKSZcTsLJFmWC0TY8TYXxFwzvul/hdtL4sv7Sw4v6207kD+ybrVh86uP1ZXs/vAlTVra5atbK67aljVJkEhkA/TJj0bs7cjvSQNyaXAJqBqXFocAiYYJ179qBrLgWwgC0heieR1mJgzD9gK5AAXlmTZ1W8635itrrdi1jJM24I5O6ZgzE5gHVCB5DgExEmRlLsv7848kr6gMnNeWVZi8VHkl6Po+N70FacmzTqMyQ/LynTDYN/01s7W+ZVLZldkzK3IHL/rIPLKsaMqf2nOqbEphzH1yoa8vmAwzg7sOxqdlUJ9RLr5pFihaZr9METeE1tfuj4PDHyNlEVuc6yBLUMYw/HjA3vVqqq2tbV5vd5AIBBdpmk64+Z2DKvSLo4x+A5mFCKdMrlj8QAAAABJRU5ErkJggg==" nextheight="927" nextwidth="1600" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-c7i2xlarge-8-vcpus-16gb-ram" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>c7i.2xlarge (8 vCPUs, 16GB RAM)</strong></h3><figure float="none" width="777px" data-type="figure" class="img-center" style="max-width: 777px;"><img src="https://storage.googleapis.com/papyrus_images/11860fb7bce5bb958ee163aef5858952.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAATCAIAAAB+9pigAAAACXBIWXMAAAsTAAALEwEAmpwYAAADcklEQVR4nJ2VT2wUVRzHfxeMRy+mHjybGE2a2CKnJiY2Svmj1HBBOUhiSLx4EawaDhgVSIiy6G4TglATCARUvOBCt2HEbbc1/UNKu9Om21K2LcsuZXc6+2bmzbz3e2+emdlSttulqf1lZg6Tmff5fd533htQSjHGCCGWbVm2TT2P+75DKXUcwzASiUQ8Hk8mk/39/X19fbquj4+Pa5o2PT3NGHPWFKVUhSXDUkqBUsowDNuyJUrbJtn03WvHjuUyGRTCCRmGYZimWbmapkkIWVxcJITQNcUYKxaLKOVINn2ju1vXdSklcOR22U5Npvaf+qip40RL864PAIbPdwVdIKqNVaVZpRQhhDF25uwv1+NxTfubUhcs22KERW5G4C2AV1sAXmsHGDoXAJAxubFCxArDNE3GWH4yPTszmZmaeLxYABQouey81Qk7Aba2wfON7QAjXZs0cBz6qJCPfHcw8s2B6LefXD17NDBwDCcAtAE0vQtbGlcMOPMQkTEuEEXY4/oGvpSWbecf5jqPfxr9/uCvkc9u/xmFfCEvUUZ7otWATRsQQhDRq4Tu2IgMRkdHqUFrAMPnu7jvp+70JkfvJrS+nljsga4rpZ7lUZ0Brm4LsnNZX/rVgPcB9Eu/9eZG4D2AN96EF1q2A/x16HAl9o0YrAIEBqZbncGeEKDND8IugOZt0NC6EyB+qCMAuO7/NphbmHuGwTDsDgEvvr3jCUBswsCyLWrQNQa/PwU0tO4AuNnxZV1AjUEdABdceCLWE9ucAQrkGBzIuFJ+HYDruYywWNUU7QFIX7z6z8JgEHLzqgw8SnmwMkR4LnetlPKlEEr5dTPgyJHi2pDXNfD9SvtSfnXp8Mc/fr3v6Lkf9u7TTp50OeerLUEowW1++sZPsB2g6R14rnE3wPiFy7dmB4KvqGkrvNTaBnD98y+olNFrp37+4/Lpi/ErR46kuxOEefAhwOtb4OX2bQAnGl4pM6wFSCkJIcWl4kx+JnVnLDUwlNd1UiotWeXMg0z63j0tOTA/NmYVS9RzJ3L6vxNjPb0j2cHBx7OzrudlH92fuD9zOzW0kMmU5uddz6sJKfgfrBQhZURefSf/MLdklIJdLFz8AkU448tTv5wzInXsmnHrAIQKXq7egoQSlY7Kpqnr+tTUVKFQWHlGYoARKoh3nY3rP+sisvbeZDIoAAAAAElFTkSuQmCC" nextheight="927" nextwidth="1600" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-c7i4xlarge-16-vcpus-32gb-ram" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>c7i.4xlarge (16 vCPUs, 32GB RAM)</strong></h3><figure float="none" width="788px" data-type="figure" class="img-center" style="max-width: 788px;"><img src="https://storage.googleapis.com/papyrus_images/d52869816daabdafd797df8560bc9116.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAATCAIAAAB+9pigAAAACXBIWXMAAAsTAAALEwEAmpwYAAADZUlEQVR4nK2V32sUVxTHD0JFsK/FPvQ/qDWNsVSKPgjBqK0/+y6ItFQQSlHUt6oYBEWqYTdQTFwk0kDtYjC6mqzZxDXJhvzQTLpDks1uTJOsu42bzezszJ25986dI7Oz2l03san0CzMDl7n3c873nDMDiEgpJTqxuCU4FYIx29YJIbqezWY7OzsDgUA4HO7v7+/t7ZVlWZKkUCg0NTVFKdUrRAjBgkRBiAiImFNyc6m5yGj/ne4+v7/97qXLyViMW5ZeYGSzWUVR3LurhYUFVVVJhSilmUyGCzEyE33Q0SHLshACbLQFF009zfAtwLYD8GHNIYDhGzecKDjH1ckNFhFVVaWU/nq96V4gEAp1E2JAXssTxfAEPVAHUF0LazYdBBhq9iEip1QIwTkX5VppBREVRaGUpsaj0/Hx2OT4y4U0MM444Q5gN8CWXbCu6lA5YDVyAbYQeU37O526euH7X84e8dQf9fvOFy3yuoCaOvig6iDAiM/3fhbphKReJBsvHvPUf+e7cjzc7oV4Ik4Uwxv0lgLeLwMsWMQ5Nws117Q8oyZMTEwItnwGjJrckWVxbr+O8V+LzMvzhpm/Zmxhe/4ni9RKgDQmMY01djWWAp7dbNGFaHvsv90TbvV3tJ0/Nzsygogr5fEuQOJ54p8uKgD2A8i/3Q7NDsAugI2fw/ptdQDtP/7kzLxhrKYGZYDKLnIBT5LDsBdgy5ewoXYPwMPTZxDRovQ/Z0BMQlVaCjgAEL31++O5QdhXBHwNEDh52umrigy45bQBpcxthGUyMEyDaWyVAJMQ5hxnFa6iLQXZ7mNFi4o1qN4Jaz5zAeH51xZ9tGOPAzhVYlHxOBtxYCLSIw12DUSfBh8lJUkn5G0AtzjT2LUHDU5Ja3bC2qq9AH+2tHZNR+AbgJov4OPa3QD3TpzShfDeuXrN33ql5f4fP5+Tg0GNM/gBYPsn8OnhrwAaNm/NUcrK6wRCCC2vZZYy8VS87+lYX2QoJcvq4uJSPhebj0UTiVA4Mjs2ls8sEtMYn49GZOlheGhmcPDl9LRhmlPJ2GhM7ugeeD4quStv1d/5H7yRquY4Z6UrqRfJpeyi+5EhxPkpVU4D55zo+rLdVQaw0NlcOsAWWm5EOUWRZXlycjKdTr95R3AHY6Hlbltp7F8BlOzLgADhOM0AAAAASUVORK5CYII=" nextheight="927" nextwidth="1600" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-c7i8xlarge-32-vcpus-64gb-ram" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>c7i.8xlarge (32 vCPUs, 64GB RAM)</strong></h3><figure float="none" width="794px" data-type="figure" class="img-center" style="max-width: 794px;"><img src="https://storage.googleapis.com/papyrus_images/6fe520d5c0dc4ff8d46d7228ab4a8bd3.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAATCAIAAAB+9pigAAAACXBIWXMAAAsTAAALEwEAmpwYAAADRElEQVR4nLWUT0wUVxzHf2nSnryaNvHgwXNjEIkXD2qJiNQWuPXU9FATPXixwR5MGxONYIxas5IQBNKQ1JCGaMwK8tfNUth2YYmuO+xmd10iK+6gyzDM7nsz79+8ZmYKGRa0qPF7mH+Z9z6/7+/3ez+QUmKMMcKMME4sITi1beR8QZqmDQ8PB4PBUCg0NTUVDocVRYnFYqOjo5lMhhCCNgljLF0JV1JKQAjlF/L5Qv6v2YngxD/37j6419qaTyYtSg3DKLrSNM27apqm67qqqrqub94dYVwsFoUQsVx8cGhIURQhBDDOBBW3Q13QBHCwEXZUNQPMdHc7UTAmtycvWCmlYRiEkI7O28GBgfHxRxibUCqXsIYDIwGoA6iqhU++bAKY7uqRUjJCxPbEGPMYuq4TQgrJRC6bzKSTxVcqUEa5xdvH2qEeoLoOPt3bBBDr6Xk/BwjhJbVw4+LJGxd+uPbr93c6fgGEETGIH9D8fwCxtt0WAIwLLxfbL58KXPqxs+106G4A5p/P0zK9NRLwO/iQFDHGLKcvMUZlxgjMpea4xSsA750iwzDYxlWQSqWIQfyA5jUHlFiMM8Y4Z859+w4qAYKJj+ggnUlXOHABv5u2HZ4OPZqZHQlNRfr71WxWSvkmH29z4B00P6ARQPnjz0jhCXwHcPAQ7KprAHh47mcHQMg7OyiVS2UN+dv0WxcwvhCFBoB9NbDzcD3AQEuL01emuTn2dQkhtnDAJa9w4AEmFmfgG4D9B+Dz2uNrDoiJ3a2cstu+0/A2B5RTWqabHTiAEy5g51fHAQZ+OudLkW1LabtPuitvDnoTbwsHtEx/G7wJxwCqj8Jne08APO29M5aLwNcA1TXwRW09QPBsCxLien/b1b7eKz33+86fTwwNY8F3n90DjfvhyJmjAJ3HGlZNk26sEwghnLG8UswWspOz8cnIdEFRjOXlldJq+kU68ezZeDiyEI+XisvYMhP5+OTTx4Oh6Hw0+jqXw5aVfJH4O/nk/thkJhJdyqRMy6qoP/hfDGOVMer/Uni5uKIte0MGY2wLJzH+7DuVZwwjtGV3bQBwyf+r2FoSueReRKu6rihKKpVSVXX9H8EcDJfcW/amU/kvh1La8g31roYAAAAASUVORK5CYII=" nextheight="927" nextwidth="1600" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><hr><h3 id="h-gas-costs-for-on-chain-verification" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Gas Costs for On-Chain Verification</strong></h3><p>Whereas Noir won on speed, it dramatically fell behind on gas cost.&nbsp;</p><p>Gas costs largely depend on proof size. Groth16 systems produce constant-size proofs regardless of circuit complexity. Noir's UltraHonk system generates proofs that scale logarithmically with circuit complexity:</p><table style="min-width: 319px"><colgroup><col><col style="width: 269px"><col></colgroup><tbody><tr><th colspan="1" rowspan="1"><p style="text-align: center"><strong>Suite</strong></p></th><th colspan="1" rowspan="1" colwidth="269"><p style="text-align: center"><strong>Average gas cost</strong></p></th><th colspan="1" rowspan="1"><p style="text-align: center"><strong>Difference</strong></p></th></tr><tr><th colspan="1" rowspan="1"><p>SnarkJS</p></th><td colspan="1" rowspan="1" colwidth="269"><p>347,665</p></td><td colspan="1" rowspan="1"><p>Baseline&nbsp;</p></td></tr><tr><th colspan="1" rowspan="1"><p>Rapidsnark</p></th><td colspan="1" rowspan="1" colwidth="269"><p>347,665</p></td><td colspan="1" rowspan="1"><p>Baseline&nbsp;</p></td></tr><tr><th colspan="1" rowspan="1"><p>Gnark</p></th><td colspan="1" rowspan="1" colwidth="269"><p>407,664</p></td><td colspan="1" rowspan="1"><p>+17% (extra security features)</p></td></tr><tr><th colspan="1" rowspan="1"><p>Noir</p></th><td colspan="1" rowspan="1" colwidth="269"><p>2,396,575</p></td><td colspan="1" rowspan="1"><p>+590% (6-7x higher)</p></td></tr></tbody></table><br><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/bdea85219f490f9d522aa7a3aee745fa.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAATCAIAAAB+9pigAAAACXBIWXMAAAsTAAALEwEAmpwYAAADNklEQVR4nLVVXUgUURQ+j9F79BRFP9CbIZI99AOpvfQShr5EDyFaWdlbUU9RFGGQFELRlosQBJVW0I9ULCYW21q2mtuO7k62bbaOwzg73rkzc+fOnRszW9u6jlRU39NwOPd85/vuOXeAMSb5ME0Tl4AQkslk+vr6IpHI8PBwLBaLRqPJkWQum42/ef3uQwJZFg4C98F8cM6BUprNZhVFIYSY84ExVkswI8tX7nV23Oo6H7rTffrM0IMHjHMD42I+IURRFMaYIInP+yMj8RFCCHDOEUKUUv4rzBkY2gBq10HlwTqA8MbNnHPXb7PQLOdc0zTG2O27PY8eP4n09yOEPAWKohQJiqmlSpkfzOsIDgPUroWKlhqAruotgQQ2pYo48UVMpVOCPD0FhBBJkiilhULFjzLwAsERgLq1UHmgBuBG9VaXc8fPL5wqEGCsX+s4dvl0U/hi28Nb7Z5FGOPFFJRb9ENBLUB409aFChBCOkLhy8dD7a09oROvn173LFJVtZjxlwSaplFKLe/KDQPrNrEgmUzKsvxnFlW0LGaRqqpl8wKTk5Omaf4rizRfwTwCURQLUxTY+GIK6vwp+i0Fmqb9X4v4r6aI/YgEWsSY43o+sYJXARYV9tumtstd6sNL/w7H8bpzHP9MoEUudVzmuoy5frkABYQQWZZtYtvYJqZhmRjZNvU7IgaxDZOYeM62HOYirJcRcM7jwruXo0ODb0ZHotHPiYSmzQVYZFuO8FU4FG7dd6nz6Kmr4YbGzwMDmmMd7T7Y1NnefO5maHdD4k6v4brQClC7Bir2Fy2qOl8F9atg+7EaWBJavSGv6+UEFrEopr1D96EeoHozLN+xC+DVqbOTRIEGgE3rYd3enQA9jXsMxqAZoKb4VGxjnC87uRK2L4XK/dUAF2DFbH7BHZimmVfzyqySnkq/TSZfvIzlBGFWkvJIS2XHhcynyGDs0+ioKknYMFJfJ+Kp5ItoPDM2NvPxo46xOJV+LwrPBmPiWGImnUYIlc2IZ9HPVdI0yzRKIzPStDyd45wb/u/BoY6/W878MXN0NFdWN4Bg4XPEGCOEeNUxHveRy+VKN6P0z1UaLCX4Bk+W8gE2XStDAAAAAElFTkSuQmCC" nextheight="927" nextwidth="1600" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><hr><h2 id="h-discussion" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Discussion</strong></h2><p>Noir stood out on proof generation time, 5-50x faster than Groth16 counterparts on the same hardware <span data-name="raised_hands" class="emoji" data-type="emoji">🙌</span> yet 6-7x costlier on gas.</p><p>Noir's speed advantage largely comes from its blackbox implementation. The black box functions are hand-coded mini-circuits that the Aztec team wrote directly in their proving system, barretenberg, that use highly specialized custom gates for specific operations. Specifically, barretenberg has an algebraic gate that directly evaluates non-native field operations efficiently. Noir calls these custom gates directly, dramatically reducing circuit size and proving time.&nbsp;</p><p>These results reveal fundamental trade-offs rather than clear winners. Noir excels in proof generation speed for certain operations making it in some cases great for applications where users generate proofs client-side on mobile or browser environments, while having a higher gas cost due to proof size. It's also ideal when development velocity and iteration matter most or when you can use Layer 2s to reduce the gas cost impact. Groth16 systems like SnarkJS, Rapidsnark, and Gnark become the better choice for complex general computations that Noir can’t optimize for, or when onchain verification costs are the primary constraint.</p><p>However, keep in mind that the ZKP landscape continues to evolve rapidly and new innovations and techniques are coming out every day. Happy building!</p><p>For raw data, code, and plots, visit our<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/lukasrosario/zk-snark-ecdsa-benchmarks"> <u>GitHub repo</u></a>. Feedback and contributions welcome!</p><hr><p>If you’re interested in helping us build a global economy that increases innovation, creativity, and freedom, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.base.org/jobs">we’re hiring</a> — and we’d love to hear from you.</p><p>Follow us on social to stay up to date with the latest: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/base">X</a> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/i/lists/1876354838289932418">Base team on X</a>) | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://warpcast.com/base/">Farcaster</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://discord.com/invite/buildonbase?utm_source=dotorg&amp;utm_medium=nav">Discord</a></p>]]></content:encoded>
            <author>base-engineering-blog@newsletter.paragraph.com (Elena Nadolinski)</author>
            <author>base-engineering-blog@newsletter.paragraph.com (Lukas Rosario)</author>
            <author>base-engineering-blog@newsletter.paragraph.com (Joe Parks)</author>
            <category>zk</category>
            <category>benchmarks</category>
            <category>wallets</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/bbdf4a4b20bc1cf44a98fcbf35af6e0e.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[L2 Primary Names are coming to Basenames:  ENSIP-19]]></title>
            <link>https://blog.base.dev/basenames-ensip-19</link>
            <guid>Y2oHEGG2eVfbX4IDSSCf</guid>
            <pubDate>Fri, 15 Aug 2025 15:55:02 GMT</pubDate>
            <description><![CDATA[The Basenames protocol will upgrade to support ENSIP-19: a new protocol spec from ENS for L2 Primary Names. With this, Basenames will become a portable identity solution for any ENS-supported chain]]></description>
            <content:encoded><![CDATA[<h2 id="h-background" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Background</h2><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://base.org/names"><u>Basenames</u></a> are the Base-native identity layer, a decentralized open protocol built on top of ENS, and the identity solution for your Base Account. Every new user that onboards to The Base App gets a unique username that fits the form name.base.eth; that’s a Basename!</p><p>&nbsp;<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://support.ens.domains/en/articles/7890756-the-primary-name"><u>“Primary names”</u></a> are a cornerstone of the ENS protocol.&nbsp; They allow any client or third party app to use the address of a connected account to reverse-lookup the name associated with that account. This is the mechanism that allows a user to look like they’re logged in as <code>jesse.base.eth</code> in the wallet pip in-app instead of appearing as a <code>0x1234…</code> address. And if you hold multiple names, you can specify which one you want to be represented by.&nbsp;</p><p>Under the hood, resolving names or addresses are called forward and reverse resolution respectively. Forward and reverse resolution are reciprocal processes and can be visualized simply like this:&nbsp;</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/237223f00ad9b9293bba45640465543f.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAWCAIAAAAuOwkTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC7ElEQVR4nK2VMUzcMBSGLbXXSqVMrDew3MBQZhYWBtLhpJI9A8fgATzULGZJh0glQ8mU6SYPZLouVtW6UhukkskDhKrc5C5Wh2zp4i6pVFz1DEe4oxAh/iGKHNuf/d7/XoBpoKqqGg5OCzSZVBSFnJJS6t4AnuchhAghGL/EGNv3brc7HA7vAVBVFULIGKO1ri5kjInjmHN+R0BZU1EUEMJ/U0eanZ0FAAghKKW+/0prXZv7sxGgKArGWJ7nQoij4+Mvh4ebW1tSyhdra67rdrvd5eVlznmSJA8ePv70Of12enp0fCyEeP+BT2f+EjD+JuX3PD8xxvw5O7OR8X2fcw4A6HQ67Xa71XpECBkMBoyx+l6DwVut9YTBLgFaa8bY15MTx3n+bHFxOBwyxsqyNMY4jiOEUEoNLySl7PV6FmC3S9N0ZWUFIWSXjBnnACklxrg/EiE7hOzYd4yxUj/iOCaE+FdFCLF72YT7vm+MyfPc8zwhxOUNLCoIAoQQG8nGN03TJEkghGEY3lBWWuvt7W3XdaMoQgj5vp9lGcY4SZIrAEKItUer1ZqbmwMAzM/PAwAWFhaCILCAaRljlFKO43ie1263n8zMAAD6/f6bvb2NjY3Rqt/nAM45QkgIkaYp59w+hRAQwjRNJ4qgDrCMUT7e2XNACO2ZJpOcZVkYhjbcm1tb/X4/DMMsy0wDVVVFCIEQ9no9e6DJJNdDPLbpeJxz3p8SpXS8SutfxpgnT2f39/f/a9PxraWUeZ6PR7TWjuNQSm3cOOfWC67rcv6xvjDLMlsHt1TyRKFVF73I+jiKojAMlVKU0oledHBwYK9yC0ApxTm395BSrq/3VldXjTFLS0udTsfajDFGKY2iqF59jLGbWkU9VupCRVG83t31PI8xBgCwDgYAQAiTJInjuCzL+uQ7tmuMcVmWcRwzxgaDQZIkUsrpEF2rRgDHcYIgoJTWXWQb1D0AbIex/qkry7Imv+VGgIb/92sBfwH9v9uh5KdIFwAAAABJRU5ErkJggg==" nextheight="849" nextwidth="1245" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Today, Basenames natively supports primary names but with some caveats:</p><ul><li><p>When Basenames launched there wasn’t support from the ENS protocol to achieve primary names for subdomain issuers on L2s. The only supported path to primary name setting required an expensive transaction on mainnet against ENS’s ReverseRegistrar contract.</p></li><li><p>Apps that want to support Basenames primary names have to use a Basenames-specific lookup, supported most commonly by our <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.base.org/builderkits/onchainkit/guides/use-basename-in-onchain-app"><u>OnchainKit snippet</u></a>.</p></li></ul><p>This means apps that don’t specifically integrate with the custom Basenames reverse resolution flow are unable to check our users’ primary names. Additionally, if a user wants to take their Basename identity to other chains like Ethereum L1 or Optimism, their Basename can’t follow them.</p><hr><h2 id="h-enter-ensip-19" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Enter ENSIP-19</h2><p>In close collaboration with the Basenames team, ENS has refined and produced a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.ens.domains/ensip/19/"><u>formal specification</u></a> for supporting L2 primary names. The ENS DAO voted to enable this protocol upgrade as of <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.tally.xyz/gov/ens/proposal/42524979896803285837776370636134389407867034021879791462477783237030656381157"><u>Tuesday 7/29</u></a> with overwhelming support from the community. With the finalization of this update, Basenames is now moving to upgrade to support this spec.&nbsp;</p><p>Once fully integrated, we get the following benefits for all Basename users:</p><ul><li><p>Primary name resolution on all apps using modern webapp clients like wagmi; not just ones that integrate directly with Basenames via onchainkit.&nbsp;</p></li><li><p>Basenames can serve as your primary name for <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://discuss.ens.domains/t/executable-enable-l2-reverse-registrars-and-new-eth-registrar-controller/20969"><u>many networks</u></a>, not just Base.&nbsp;</p></li></ul><hr><h2 id="h-how-ensip-19-works-under-the-hood" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">How ENSIP-19 works under the hood</h2><p>ENS deployed a new contract called the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/ensdomains/ens-contracts/blob/staging/contracts/reverseRegistrar/L2ReverseRegistrar.sol"><u>L2ReverseRegistrar</u></a> to each supported chain. These contracts let any address set its primary name for that chain. When a user connects their wallet to an app on one of these supported chains, the client can begin the reverse resolution flow for the user.</p><p>Let’s explore how this works&nbsp; by following a reverse resolution request for Jesse connecting to an app on Optimism:&nbsp;</p><ol><li><p>Jesse connects his wallet to an app on Optimism which kicks off the ENSIP-19 reverse resolution flow.&nbsp;</p></li><li><p>A reverse resolution request is sent to the ENS Universal Resolver on L1 asking for the primary name of Jesse’s address for the Optimism network.&nbsp;</p></li><li><p>The resolution request is forwarded to an L1 Resolver contract responsible for reverse resolution on Optimism specifically.</p></li><li><p>This request kicks off a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-3668"><u>CCIP-read</u></a> flow which delegates the resolution to an offchain Gateway.</p></li><li><p>The Gateway queries for the Name set in the L2ReverseRegistrar associated with Jesse’s address on Optimism and constructs a state proof for the fetched data.</p></li><li><p>The Gateway provides this response to the client so it can call back into the L1 Resolver with the data and the state proof used for verifying validity.</p></li></ol><p>The client now knows that Jesse’s address is associated with jesse.base.eth, but we’re not done! When setting a name on the L2 Reverse Registrar, this contract cannot verify that an address is setting its primary name to a name that it controls. As such, there’s a close-loop validation step to the ENSIP-19 flow, very similar to the existing reverse resolution flow which verifies the retrieved name forward resolves to the original address.&nbsp; &nbsp; <br></p><ol start="7"><li><p>The client now requests the forward resolution data for jesse.base.eth for the optimism network which is configured in the Basenames contracts on Base in accordance with ENSIP-11.&nbsp;</p></li><li><p>Through a similar CCIP-read flow, the forward resolution data for Jesse’s name is fetched from the Basenames contracts.</p></li><li><p>If the address connected to the client matches the address returned by the optimism-specific forward resolution request (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.ens.domains/ensip/11"><u>ENSIP-11</u></a>), then the Primary Name is valid and Jesse shows up as jesse.base.eth!&nbsp;</p></li></ol><p>A complete swimlane for this flow can be found in the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.base.dev/basenames-ensip-19#h-appendix">Appendix</a> below.&nbsp;</p><hr><h2 id="h-next-steps-for-basenames" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Next steps for Basenames</h2><p>With the ENS contracts, gateway and tooling integrations live, modern clients will be capable of using ENSIP-19 to reverse resolve any Basename. However,&nbsp; Basenames user data is missing two pieces to support these requests:&nbsp;</p><ul><li><p>Basenames users today write their reverse resolution data straight into a generic&nbsp; resolver contract deployed before ENSIP-19 was finalized. This data instead needs to be located in the ENSIP-19-defined L2ReverseRegistrar to be available for the ENSIP-19 lookup flow (steps 3-6 above).</p></li><li><p>The ENSIP-19 flow requires that users write network-specific data defined by <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.ens.domains/ensip/11/"><u>ENSIP-11</u></a> for each network where they want a name to be primary. Many Basename users today are missing data in this format for any network other than Ethereum mainnet.<br></p></li></ul><p>Since existing Basenames user data exists with accurate content already, but is located in the nonstandardized combination of contracts or storage slots, the Basenames team is taking a proactive approach. We are choosing to migrate the data on behalf of all users. In so doing, users won’t have to return to a Basenames surface to migrate their own data and will inherit the benefits of ENSIP-19 as soon as the migration and integrations are complete.</p><h3 id="h-new-contracts" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">New Contracts&nbsp;</h3><p>To support these efforts, we are deploying three new contracts. Each of these contracts is undergoing rigorous testing today and will be externally audited before going live.&nbsp;</p><h4 id="h-upgradeable-registrar-controller" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Upgradeable Registrar Controller</h4><p>Today our existing RegistrarController manages the business logic associated with Basenames. It handles registrations, discounted registrations and renewals. Since it can register new names, it has unique permissions within the protocol. Specifically, it is allowed to write records to our immutable BaseRegistrar contract which tokenizes unique names and handles expiry. This upgradeable version will do everything the existing registrar does and will also write ENSIP-19 compliant records on behalf of users during registrations.&nbsp;</p><h4 id="h-upgradeable-l2-resolver" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Upgradeable L2 Resolver</h4><p>The L2 Resolver contract currently deployed is immutable. Though it inherits all of the most modern ENS resolver profiles, the Basenames team has plans to add functionality and features in the future. We also need a new Resolver target for our new Upgradeable Registrar Controller. So we’re taking this as an opportunity to get more data into a resolver which can accept new functionality seamlessly. Functionally, the only change to this contract over our existing L2Resolver is upgradeability.&nbsp;&nbsp;&nbsp;</p><h4 id="h-migration-controller" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Migration Controller</h4><p>This temporary contract is designed to assist in the forward resolution data migration.&nbsp; It has an admin-only hook that can be used to set ENSIP-11-compliant forward resolution data on behalf of Basename users. The primary motivations for building this hook into this contract are twofold:&nbsp;</p><ol><li><p>It has a planned obsolescence at the end of this project</p></li><li><p>It has permission to write records on behalf of users in the L2Resolver contract where the forward resolution records need to land.&nbsp;&nbsp;</p></li></ol><h3 id="h-migration-steps" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Migration Steps</h3><p>There are two large data migrations that need to be accomplished to convert all existing records into ENSIP-19 compliance. With over 750,000 Basenames active today, we need to perform a large scale series of transactions over multiple blocks. To ensure this migration doesn’t cause network congestion or spike gas costs on Base for everyone, we’re going to throttle the number of records we write per block to ensure our activities will have minimal impact on the gas market for other users.&nbsp;</p><p>Since the migration itself cannot occur atomically and will instead take place over 3000 blocks, our contracts and surfaces need to be configured in a way to support a zero-downtime switchover. To accomplish this, we are leveraging two strategies:&nbsp;</p><ol><li><p>Set up a new, parallel registration path through our new Upgradeable Registrar Controller which can accomplish both legacy and ENSIP-19 reverse resolution writes.&nbsp;</p></li><li><p>Similarly, perform “dual writes” when users opt to change their primary name ensuring legacy and modern reverse records are available to integrators.&nbsp;</p></li></ol><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a0c9512678bc9678f2e4088f4c07fa69.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAUCAIAAABj86gYAAAACXBIWXMAAAsTAAALEwEAmpwYAAADH0lEQVR4nNWUa0hTYRjHj9YnyY8VmRDN2yj74EBtU8S5lpmiGUkrKZqmNKcj8bYTCStjZRfNnF2JSX0IjGFM8H7Z4qi72sSWw206yYUu8PSh0/TV7Y3tmLhhoklEfx4OL7wHfu/z/z/vi8C/LOSfAYBXcEVu3/KTe4Mf/rADgiBwr9YcYosdSCQSLpcLADAaxwYH3+lHOlXqNu9X3t0tj4uLZzDoSUlJMplsQNGqUrd5d9v1I+39irdTU5Z1AAAsAbBELiCEcrmcy+XabNMxMUcyTh0sFjDKqpJLy5KzzxwQCvNj444ymcz09PRIKvUKn4lWHy8tY+YXxlYIWY8kFzGs55d1G2ZgMBgghCJRdYUwrfYep1HCbWzKu1Z9Qtp8H0Jot88AAOrqH1ah2VIpX9JUUCPOaZDktchQDOvzAbjcnhWmMyLIXgRB0Lpmtxt8mZ1VKpUAABzH9XrMMDqo1Sn0ekyl6rNYTRBCs9lMED8ghMOqAa1OodUpPoy81+uVQ8M9Dsecj0ULi56gim4+Rkjto7tcTkyj0Wq1vrPkI7vdTgI2G/J350IaTxSTc/Vk9tnkxMSikhIGg5GVlTU75zkOAAvORSdZJPLzzAxBEKsDvVb+ADLVro4hsoHzuejsV3tHX/+AYsDhcPzuaDabDce/baoDL8DN599esQiJdLmcr1pabLZpj8Va7Z0ntRmczAxOJjODlV96ubO/y5uBBQBAEISAW47ybqA8EcoTXS+5JbhUNYSp17GIIJwFhTWnc8pNJhuE7lHT+KdJ69jERGbehTBGhIe7G0F2IsGUYH5liUalefrsuVze1tAg2b8jLDSAengPPQQJD0WiaCFJb163rBrjN6YrF31+fv5QdDSLfexBXX3DyxfnBLnCOpTNSS2/W1lcI2BnpgYGBAbtCkpJSTGbLQwqMzEqNSGSTY9gxYenJESyW2Xy9QGrdw3HcQqFQqPRent7PW5YzZZJq2XSOmExW6en1Bq1WCyWSqVGoxFCaPw4vlrjxgnDyCgZvr9FfiKfGrhtbeqxc7ldfrV2HJeXlv1qywC4Df3/gJ+vCmcEFA7mQQAAAABJRU5ErkJggg==" nextheight="975" nextwidth="1600" class="image-node embed"><figcaption htmlattributes="[object Object]" class=""><em>Registration flow for both The Base App and Web, showing the dual-write strategy enabled by UpgradeableRegistrarController.</em></figcaption></figure><p>Once all new registrations and primary changes are ENSIP-19 compliant we can safely deprecate the legacy registration paths. This deprecation ensures no data will be missed when we migrate state.&nbsp;</p><p>First, we’ll migrate user forward resolution data. By leveraging our new Migration Controller, we can safely copy the data that exists for users in the coinType == 60 address field and move it into the Base chain-specific slot at&nbsp;coinType == 0x80002105. This means that our users will now have Base-specific forward resolution data available for the close-loop steps of ENSIP-19.&nbsp;</p><p>Second, we can begin moving the reverse resolution data from our L2 Resolver to ENS’s L2ReverseRegistrar. ENS was kind enough to include an admin hook for us in the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/ensdomains/ens-contracts/blob/feature/simplify-reverse-resolver/contracts/reverseRegistrar/L2ReverseRegistrarWithMigration.sol"><u>Base implementation</u></a> so that we can safely migrate the data on behalf of our users.</p><p>Once both data migrations are complete, the Basenames technical work is complete! At this point we will start working with integrators to switch over to using updated tooling (wagmi, ethers, viem) which supports ENSIP-19.&nbsp;</p><hr><h3 id="h-conclusion" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Conclusion</h3><p>We’re well into the development cycle for this project and our most recent batch of contracts to support this are currently under external audit. At current pace, we’re targeting an early September data migration with earlier cut-overs to the new registration and set-primary flows.</p><p>&nbsp;If you run an app on Base and either integrate with Basenames today or want to in the future, we’ll have updated docs published along with the public announcements soon. If your app integrates with Basenames today, rest assured our migration is initially backwards compatible. However, you will need to upgrade when we deactivate legacy data writing for the sake of saving users’ gas.&nbsp; We will contact our ecosystem with details as integrators must upgrade.&nbsp;&nbsp;&nbsp;</p><p>That’s all for now! If you don’t already have your basename, register one today at <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://base.org/name"><u>https://base.org/name</u></a>. And please stay tuned for updates from the Base Account squad as we roll this out.</p><h2 id="h-appendix" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Appendix</h2><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/6f6a26f78cc749c8dcfd416f40dff3b5.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAVCAIAAACor3u9AAAACXBIWXMAAAsTAAALEwEAmpwYAAACuUlEQVR4nJ1VPWjcMBTWmq3gLaOnGz16TKFD0slDB291R08J3iq6eehi2knQTauWUhCFUEQzlJdVU4p2EULA0wU0XUEl/lLh5HxJ6Dc9y0/ve/9iIYT1zXrzZxOfh82k+fb4HWPs05fP6WQX2PwjhDCOYwhhWy/8O1zfrG+vvWDA+a9znEAnqSWZvXzzijH29fu3GKO1VillrY0xElFVVVrrGKP3XilFRNARQhhjrq+uz36ecc6TjpwA60IIKWUI4c6R9x8/xBhPf5wOwwBDRLRarZRSMUbnXNd1uKy1fn10dHxycnl1efH7oq5rzjmImwkgqKqqaZpbAnxrraWU3nshBOe86zohhPd+HEeEPE+dcy79WkzLvRRt/mzWN+txQoxRCHEXFGNweV6eJMenkHTuFRmeSinbti0mZFlmjIkxGmOKouCc9xOe5NhJ4JxDQtu2hS1r7TiO1lrnXNu2SqkUyn9GYIzpuo4xlud5qvNz0vIsAmstCpBlmVJKSpkIiEhKqbWek+0i3klARIeHh0VR1HWtlHIT7IS6rrMsa5rGz2CtfXwwF2rgvUeTee+dcyGEpmkYY1VVYUTQowkPjM6bdbkG4IB17z0R5RP29/frunbOwW7YgvcezVYUBRGB+yHBdn7DdBNep+AW845m45wPw4CuW44geTSfvtVqha6VUvZ9L4RYjEAp1bYtET1WA5ADfnJcKVWWZZZle3t7TdMIIbTWUCCiFNa8KjsJdrXgOI5lWeZ5fnBwgHWbzrXWfd9jp27fXYhASomVYIxJ2ScijAVymJxN/fpgvJcJkI26ruFs2grjVL0kI3VJTqbBlByFfI/AWqu1RgastWVZpscEjwzOOedCCMgoOywOw9D3Pdzvug47kaUewBjj5cJi4JxjspxzWmuc4+UyxoQQYBQvF5xI3ZVetL+zD443PAHDbQAAAABJRU5ErkJggg==" nextheight="1037" nextwidth="1600" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><br>]]></content:encoded>
            <author>base-engineering-blog@newsletter.paragraph.com (Katzman)</author>
            <category>applications</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/eeea33611f593d67ff0d507d70e55fd1.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Introducing free security reviews for Base builders]]></title>
            <link>https://blog.base.dev/free-security-reviews</link>
            <guid>rcyL3EUXctJtW3hl710J</guid>
            <pubDate>Tue, 29 Jul 2025 18:31:49 GMT</pubDate>
            <description><![CDATA[Today, we’re excited to launch free security reviews to a select cohort of apps and protocols on Base.]]></description>
            <content:encoded><![CDATA[<p><strong><em>TL;DR: Base’s decentralization roadmap focuses on transparency and enhancing onchain security. To support this, we’re committed to fostering a secure environment for onchain builders. Today, we’re excited to launch free security reviews to a select cohort of apps and protocols on Base, in beta. In the initial phase of the beta, selected teams will receive a scoped review, security-related recommendations, and assistance to improve security posture at no cost. </em></strong><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.google.com/forms/d/1it_fBGU_xKn5p1XvXx8XFhQUiyr_rDt4mTW7HZJ8zgw/edit"><strong><em><u>Apply now</u></em></strong></a><strong><em>.</em></strong></p><h2 id="h-bringing-coinbases-security-best-practices-to-base-builders" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Bringing Coinbase’s security best practices to Base builders</strong></h2><p>From day one, Base Chain has been built with a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://base.mirror.xyz/_SiAx8dQRYJJ4lygkJc0QvBKvf16VVm7_-mdWMdFv20"><u>security-first approach</u></a>, leveraging Coinbase's enterprise-grade practices to secure the network.</p><p>To bring the next billion users onchain, we’re now looking to empower builders with the tools they need to build secure, reliable apps and protocols for their users. Last year, we introduced <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://base.mirror.xyz/dbmJkcsc1RPaWyqn7R5vhVJFRWoy1CvmlXeuUwsfTXk"><u>free onchain monitoring</u></a> for Base builders, powered by <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.hexagate.com/"><u>Hexagate</u></a>.</p><p>Now, we’re taking another major step towards bringing best-in-class security practices to apps on Base. Today, we’re excited to launch free security reviews to a cohort of apps and protocols on Base, in beta. Selected teams will receive a scoped review, security-related recommendations, and assistance to improve security posture at no cost. Spots are limited—<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.google.com/forms/d/1it_fBGU_xKn5p1XvXx8XFhQUiyr_rDt4mTW7HZJ8zgw/edit"><u>apply now</u></a>.</p><h2 id="h-empowering-builders-on-base-with-proactive-security-reviews" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Empowering builders on Base with proactive security reviews</strong></h2><p>We’re extending our security offering upstream by proactively supporting builders in their security practices—reviewing their smart contracts during the development process and helping to identify and mitigate some of the potential vulnerabilities before they become critical issues.</p><p>The Coinbase Protocol Security team has conducted extensive audits across various DeFi protocols, including many on Base, and has started developing a streamlined process focusing on high-risk areas, leveraging static analysis and cutting-edge AI-based tooling.</p><p>This new beta program provides complimentary security reviews to help builders strengthen their protocols. This initiative leverages the expertise of Coinbase’s Protocol Security team (ProtoSec), offering builders the opportunity to benefit from our deep experience in securing onchain protocols.</p><h3 id="h-how-it-works" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>How it works</strong></h3><p>Selected builders will receive:</p><ul><li><p><strong>Smart contract and security practice reviews:</strong> Coinbase’s Protocol Security team will perform targeted reviews focused on identifying certain critical vulnerabilities using advanced static analysis tools and AI-powered insights.</p></li><li><p><strong>Security-related recommendations:</strong> Builders will receive clear, prioritized recommendations tailored specifically to their protocol's unique risks.</p></li><li><p><strong>Ongoing support:</strong> We’ll actively engage with builder teams throughout the review process and offer best practices to enhance their security posture.</p></li></ul><h2 id="h-building-a-more-secure-onchain-future" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Building a more secure onchain future</strong></h2><p>We’re committed to fostering a secure and transparent environment for everyone on Base, and will continue expanding our security offering to empower more builders to deploy secure onchain apps and protocols.</p><p>In this initial phase of the beta, we'll accept a limited number of applications, gradually expanding the program over the coming months. If you’re selected, you’ll receive direct communication with details on next steps. <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.google.com/forms/d/1it_fBGU_xKn5p1XvXx8XFhQUiyr_rDt4mTW7HZJ8zgw/edit"><u>Apply here</u></a>.</p><hr><p>And if you’re interested in helping us build a global economy that increases innovation, creativity, and freedom, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.base.org/jobs"><u>we’re hiring</u></a> — and we’d love to hear from you.</p><p>Follow us on social to stay up to date with the latest: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/base"><u>X</u></a> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/i/lists/1876354838289932418"><u>Base team on X</u></a>) | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://warpcast.com/base/"><u>Farcaster</u></a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://discord.com/invite/buildonbase?utm_source=dotorg&amp;utm_medium=nav"><u>Discord</u></a></p><p><strong><em>Disclaimer: </em></strong><em>While these reviews are a powerful resource, they’re designed to complement, not replace, formal audits and other security practices. Builders remain fully responsible for their security and should pursue comprehensive third-party audits from formal security auditors. Coinbase does not provide any guarantees as to the accuracy of the reviews or any suggested mitigations. Selected protocols will be required to accept applicable terms of service before enrolling in the beta program.</em></p>]]></content:encoded>
            <author>base-engineering-blog@newsletter.paragraph.com (Base Engineering Team)</author>
            <category>security</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/89459c1540c4037efa831e58a1efb354.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Introducing the Base Account SDK]]></title>
            <link>https://blog.base.dev/base-account-sdk</link>
            <guid>g1dq2jzeQaR9QbV7w4Zt</guid>
            <pubDate>Wed, 16 Jul 2025 22:00:02 GMT</pubDate>
            <description><![CDATA[Introducing Base Accounts and the Base Account SDK, an evolution of smart wallet that enables Base Pay and Sign in with Base]]></description>
            <content:encoded><![CDATA[<p>TL;DR The <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/account-sdk"><u>Base Account SDK</u></a> is the next evolution of the Coinbase Wallet SDK. It delivers Sign in with Base and Base Pay, universal sign on and payments for the open internet.&nbsp;&nbsp;&nbsp;</p><h2 id="h-what-is-base-account" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">What is Base Account?</h2><p>Base Account is the onchain account and identity layer powering the new Base app. Onchain, it’s built on our Smart Wallet platform and Basenames. Offchain, Base Account provides a private and secure place to store personal information like name, email, phone number, and shipping address for easy sharing with apps.&nbsp;</p><p>Every existing Coinbase Wallet user with a Smart Wallet has a Base Account on day one – over 185k accounts. Every new user of the Base app will be using Base Account by default. We will be sharing migration paths to Base Account for EOA users over the coming months: see our <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://docs.base.org/base-account/guides/migration-guide"><u>migration guide</u></a> to ensure all users can continue to use your app.&nbsp;&nbsp;</p><p>Users can manage their Base Account within the Base app or at <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://account.base.app">account.base.app</a>.</p><h2 id="h-whats-new-in-the-sdk" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">What’s New in the SDK?</h2><p>The Base Account SDK is streamlined and has new tools that make it easier than ever for builders to make amazing apps. All of our documentation can be found <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://docs.base.org/base-account/"><u>here</u></a>.</p><h3 id="h-pay" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Pay</h3><p>There is a new <code>pay</code> module in the SDK, with simple functions for onchain USDC payments.</p><pre data-type="codeBlock" text="import { pay } from '@base-org/account/pay'; 

const payment = await pay({ amount: &quot;25.00&quot;, &quot;0xYourWalletAddressHere&quot; });

const response = await getPaymentStatus({ transactionId: payment.transactionId });"><code>import { pay } from '@base-org/account/pay'<span class="hljs-comment">; </span>

const <span class="hljs-attr">payment</span> = await pay({ amount: <span class="hljs-string">"25.00"</span>, <span class="hljs-string">"0xYourWalletAddressHere"</span> })<span class="hljs-comment">;</span>

const <span class="hljs-attr">response</span> = await getPaymentStatus({ transactionId: payment.transactionId })<span class="hljs-comment">;</span></code></pre><h3 id="h-ui-components" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">UI Components</h3><p>The SDK now comes with UI Components for standardized buttons:</p><ul><li><p>“Sign in with Base” or “Base” for authentication contexts</p></li><li><p>“Base Pay” for payments contexts</p></li></ul><pre data-type="codeBlock" text="// Also available in Vue/Preact/Svelte
import { BasePayButton, SignInWithBaseButton } from '@base-org/account-ui/react';

function SignIn() {
  return (
    &lt;SignInWithBaseButton 
      onClick={handleClick}
      align=&quot;center&quot;
      variant=&quot;solid&quot;
      colorScheme=&quot;system&quot;
    /&gt;
  );
}

function PayComponent() {
  return (
    &lt;BasePayButton 
      onClick={handlePay}
      colorScheme=&quot;system&quot;
    /&gt;
  );
}"><code><span class="hljs-comment">// Also available in Vue/Preact/Svelte</span>
<span class="hljs-keyword">import</span> { <span class="hljs-title">BasePayButton</span>, <span class="hljs-title">SignInWithBaseButton</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'@base-org/account-ui/react'</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">SignIn</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&lt;</span>SignInWithBaseButton 
      onClick<span class="hljs-operator">=</span>{handleClick}
      align<span class="hljs-operator">=</span><span class="hljs-string">"center"</span>
      variant<span class="hljs-operator">=</span><span class="hljs-string">"solid"</span>
      colorScheme<span class="hljs-operator">=</span><span class="hljs-string">"system"</span>
    <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
  );
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">PayComponent</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&lt;</span>BasePayButton 
      onClick<span class="hljs-operator">=</span>{handlePay}
      colorScheme<span class="hljs-operator">=</span><span class="hljs-string">"system"</span>
    <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
  );
}</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a1faeda353ee03c9a1e0e9847b7baadf.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAASCAIAAAC1qksFAAAACXBIWXMAAAsTAAALEwEAmpwYAAADVklEQVR4nJ2V72scRRjHB5uGe+2rBPtK8NIYGqFtfNMWSl5EBN/UN/aV/4ER8s53re9FX4lQWpGWntyPtCpWEszRrhetojapSdpet3fh7nZ39sc8Mzc7O7M/kj3ZO/tGUsh2eFiGGZgPz3zn+11ku/TAMjHJJh4bFXag27OwA8NdApQTyl1gLjDbJT0TP9/6f6EDVwlw23G5L6QKhZRChQTgWattYkKZDwBxHKfpYDSUUpZl2R7NAXCBSxVKGQkZSBllpULuC0I5ZVzX9dXVVV3XLctst1uUUqUUZfzAJl4ICIanx1HqMRpE4kmz9VRvUy6A8mPHXpt5cwYhdOSVIydmTiCEisWikNFhAdglBHwQxOp377fr3WDr4c7O6poWJ4kLnAAvFI7euH6r+MbxhYV3v75aRghNTEwIFeXsQEmX8c83Przz6MtbtTp2HBWGGYDy8fHxarl6fGrqnYWFUqmEEJqcnMwLYIGUSu39atS+uPGZ9mhlsDfgItOAQL9QKNTXfn5rdvb9Cxe+/+72SwKkjPqhuLZy/dv6NQi7UiZCSkI5ZB2MXb3yzXLt9sUPLro2IIQKhYKKkxwA22Nxkuy2za/KVz7dmf0dV0K1L6R0gfW5RMOxvfX08qXLH3/0yT+bm2/PzRHK8z3TQMnGX+t/6utb3spj+w+P21JGLjDK/LGxsRFjenpq+J0+deoksEMDsANAOe0zh9syUdnth4wEThju2R6lXCwuLp47d/7MmbOnT8/Nz88Xi1NLS0u+kPk02Njc1LT1vx9s3Gtod+9qjcZvu50usMwHQvSTJPrPx4NBmqZCcDi80bBLgfEf7vxUqS7f035Zq9fL5UqlWmu1d7OcoLzV8tptgXFomqFtx80mbG13KPfzdaA11n/MGLWbpVK1tlwuVyxsA8uiAp1/gFAdvdpAr99HRzWE6sX3HgopXsIHEigFyjySqeKLYCgyd53QNKNeLzAM3usFGMeERPmyyCP9NE263bDTcYGCYZi+L/bTNJOf+VLJOFGMMwfAY0xEIRUqhwYjH0Rx3Ofxk+az7Z3tx81mFqpxYnsMKN9PB3GSGiZu6rudniXjVMYpobkAGYMSSk3sdHuWYdlZ3D9P/OF/hhsm7hnYsOxRfrzonH8BJwWt8QP3x2gAAAAASUVORK5CYII=" nextheight="900" nextwidth="1600" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>See our UI Component <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.base.org/base-account/reference/ui-elements/sign-in-with-base-button"><u>documentation</u></a> for more info.&nbsp;</p><h3 id="h-sub-accounts" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Sub Accounts</h3><p>Sub accounts are live on mainnets in the new SDK. Sub Accounts have been live on testnet since February. They unlock seamless in-app transactions and allow users to aggregate and manage all of their app accounts on <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://account.base.app">account.base.app</a>. See our Sub Account <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.base.org/base-account/improve-ux/sub-accounts"><u>documentation</u></a> for more info.&nbsp;</p><h2 id="h-our-vision" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Our Vision&nbsp;</h2><p>Our mission is to build a universal and self-sovereign account for the open internet. We want to provide users with an account <em>they </em>control and that works with them across all apps and chains.</p><p><br>We want the Base Account SDK to be the easiest way for developers to build onchain: connecting them to thousands of funded accounts, with quick and easy ways to request sharing of things like email and shipping address.&nbsp;</p><p>We’re just getting started, and there’s so much more to do: still day 1. We’re excited to build with you.</p>]]></content:encoded>
            <author>base-engineering-blog@newsletter.paragraph.com (Wilson Cusack)</author>
            <category>wallets</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/c1bed3e6254d478fa404e4ed408f5aff.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Scaling Base in 2025: Sustaining 10x growth while maintaining sub-cent transactions]]></title>
            <link>https://blog.base.dev/scaling-base-sustain-10x-growth</link>
            <guid>889BK4qRSxfu04XWUCU4</guid>
            <pubDate>Wed, 18 Jun 2025 18:19:26 GMT</pubDate>
            <description><![CDATA[As Base grows, we’re seeing breakout apps that bring 10x more traffic to the network. This post shares our learnings as we keep scaling Base, ensuring that we continue to meet growing demand with greater throughput while maintaining sub-cent fees. ]]></description>
            <content:encoded><![CDATA[<p><strong><em>TL;DR:</em></strong><em> To build a global onchain economy, we need to scale. As Base grows, we’re seeing breakout apps that are bringing 10x more traffic to the network. We build in the open, and want to share our learnings as we keep scaling Base, ensuring that we continue to meet growing demand with greater throughput while maintaining sub-cent fees.&nbsp;</em></p><p>Base’s mission is to build a global onchain economy. To achieve this, we’re committed to continuously scaling Base so it can serve as a platform for billions of people to come onchain.</p><p>Over the past month, we’ve seen more onchain activity than ever before, with various breakout apps bringing 10x more traffic to Base. For instance, Base recently saw blocks with over 1.5k transactions per second during the Solace launch on <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://app.virtuals.io/geneses"><u>Virtuals</u></a>, with median fees of remaining under 5 cents:</p><figure float="none" width="535px" data-type="figure" class="img-center" style="max-width: 535px;"><img src="https://storage.googleapis.com/papyrus_images/43a922c33ada0cc2e6941f32a43b060a.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAPCAIAAAAK4lpAAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC50lEQVR4nK2SW0hTcRzH/z31GJWoIZrUWxARtVQis6cukF3oRQL1IS9J9aBZ6EZ5Q3a01JC5I+YZ7gbq1mDNhEYRNkpXD3q6zM3BNnFMh+38z2LnbLKdX9jRNSy6sL78Hs7/x+/w4ffhh2pq6xBCObl5mVl7duzcffiIJCMrWyIpFDuZWdk5uXkIoYKi4wCAMeZ5PhqN/U0BgMvlRjabraW1tVkqa5bKenp7SXKwvqFxzGBQDAy0t3fI5QRJDhqNT7Q6fZhl8b8kzLKOeSfCmIWfIvLTj8+3iKLR2PTMzDClAgD+v2YtFvN4vOuA2dlZkhwUAd8XEjr7VYoRA4AQ4bh0AVsUcRwPAPnFlxBC6bvy+RaRy7WAMbbZbElFa7FYfVsfQkhjmBAZaW2gGBig6Q8arTYJCDG4+k67xjBx8HQZ/cllnZr2B4L+QDDErN8oz/MRjsOYjXBcPBEXrf4OMEypMMZbLNVJCQDYdqC4iVAM6Uxo+76MgnP7T172LgUcbs/K6hdx0vrartQYxQ/vUuDXirQ6PU3TDocjScaY7exXAQhm65Q/EASAOimxLi1fgtCuivoWtPeo5Hy5Uj1eUd9y4krVoTNXEco4W34LQEhu+WODlZVgb9+jhtuNAPA1EuF5ft7t6RnSAgiiB7xpIMJx9OeFEIPn3Z437+f6hvT+QPAd7VCPW4wTL+8/GIwn4hGOS62tV5QQBAAYt7ygRs2pJ5QQhIQgiM/Uu4pGY+IvAHBD1rUaCv1CkQgIMXjUbKVGzdSoubZJ7nB7kjz4U8QxxYihs1/17NVbjWmyi9Q0EQqNadJosW4AhimVfsxIqbWPR3TKIVWzVKbV6Z9brclSKEk50Z3aMT+1pI6NGUxVN+9eLKusvHZd1kbc6+h+2E9Sau0GAGPMMCGGCYVZ9iM9d6ywaJhShVl2eTNyOVFdU+v1esUnxthut5eUnFIoSZ7nxT6AoFQqS0svBFeWwywTZhmn0/kNnTuunJcQ80wAAAAASUVORK5CYII=" nextheight="274" nextwidth="566" class="image-node embed"><figcaption htmlattributes="[object Object]" class=""><em>TPS on Base during the Solace launch on virtuals at ~11am EST May 29, 2025.</em></figcaption></figure><br><figure float="none" width="596px" data-type="figure" class="img-center" style="max-width: 596px;"><img src="https://storage.googleapis.com/papyrus_images/106ee860bc06247d19c4fcb97a0c26c8.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAALCAIAAACRcxhWAAAACXBIWXMAAAsTAAALEwEAmpwYAAACkElEQVR4nGO4cPHiypUrt2zZum/fvi1btr58+fI/BtgHktqyb9++Cxcv7Nt34Ojx419wAEy9DKtWrfLx87e1s09MSpKSltmyZev////hSr9+/QZSBANJyanxicnpmTn/iQYMR48fX7Vq1dSp0zo6u5YtX45V0dSp08rLy6OjY7JzckPDI2xsbZOTk01MTT09vT08PD09vR0cnBKTkvwDAvftOwBxHxwwbNi0qaOzKy+vICkpOS8vD+JkNJCUlBAUGKCmqmxjY6mupiorKxMeHi4mJurp4WZhbmZhYW5hbhYUGBAaHHBg/z50H8yfvyAvLy8pKTkpKdk/IPDo8ePI0n/+/v3//7+Murmkmpm9Z5iaoYOJva+7px8DG39RZYuepbuNe6hHYLyepbu5o7+Kvp2IgoGcpqW5hQ0kSO3t7Rlu37mzb9++7u6ejs6u7u6e23fu/Pn7Fxn9//9/1tylLR2T07LL07LLK+o6ikory8or12/amVNUW1HX0dIxOaeorq6lLzmzrLSmrbFt4qzZc2rrauvq6+fMncuA5iM00//A7MAEuMTRg2jDpk3ZObmlZeX+AYEbN23GqigwMFAcDGRkZOvq6318ffv7Js5ePpOBgaGgLufIyTNhmalZVeXZ1RVROVmL16/5////j+8/oBacPHlq6tRpc+bOr6uvP3nyFFYLurq6wiMigoKCoqKi+idOzMrOXrJ4eWNXXXByQF139dlLVwrqqhon9DT0dkfmZMxZvhTFB7v37Jk6ddqWLVvmzJ175coVXBb4+fsHBgaWlJYSEyzoFuTl5ZWUliYlJUNSMSZITk4WFhERFxf38PD4////q1evv3z58u/f39+/f/7+8xs54X/89AktPwMA+fD0HHRw8XcAAAAASUVORK5CYII=" nextheight="239" nextwidth="719" class="image-node embed"><figcaption htmlattributes="[object Object]" class="">Block with over &gt;3k transactions in a block (~1.5k TPS, given 2s blocktimes)</figcaption></figure><br><figure float="none" width="600px" data-type="figure" class="img-center" style="max-width: 600px;"><img src="https://storage.googleapis.com/papyrus_images/16dc399e8be863f646ea7aaeb6b582cc.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAUCAIAAABj86gYAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFBklEQVR4nI2Ue0xTVxjAzz/LlumWxeoUmdWMzGy6KG4UHJCYboz6YMUYYJpi8E4lsMGKwGpRpkA7VwXFLoW19XEJSgFLqbrOB72g9PLa7UrgjiKXymxLlSsN9aJraxtsF6hgmaj75eTm5Oac7/ed1wf807hfCjWNfXR0oL+foiiSJM2zIUmSmo3b7Qb/R0BN452YuNLQkBQdbRw0GfR6s9mM4zhBEBiGEQSB438hSHOgoShq7DNiGPZM8CLcQTzx+f5sb49YHKJQKN4PC5s3b154ePj8+W8xmUwabWHcF3H2Ubuxz2gbtiFI8y5ot1j8yysE7tk4HI7e7u61tEWn5LLtKSmxsbFpu6D4+Pi8/Hw2O7GouNjv9ztdLofD0apDDxcLYLgKzGzWKwUURQUEH7z+xmE+P+zNt0OX0UOX0Vd+uOrrHam2eyMdXX9cUKpu6tq2JCQCAJYsWVpw4ABQqRqTk5MFAuGcx0BNEzjSeyMj7a2t7wEg+kkIy0rZ7MTtO3akp6fDMBwYHEhUo9Gkp6dnZGSWnxSDwsJCkiRfnv7D8fGAgyTJTp1uBQBlpSKlQsKaAoIglUoVPFGr1WZlZ/8sEm1PSQE0Gg1F0ZcLvBMTTpdr2Gq1j43BEgmDvqKsVIQgqo0bN7HZiRwO5/SZ08ETNZorEATx+AXfJCcDGm3hnIJA6MePPWN2u/pcFdbWSpKky+M5WsD/LGyF8IjwpFKalJTE4XAgCFIoFM+vgMfjVVZKAJ1O12q1L07fdWx/3taoT1I/j23XXtMbDHFRUasX04SCEpX29/VR65lMJofDeZGgvLwMREZG4jg+p+CJz2f9e2hvAmvnl8xVoaEFuflKtXr5u4sAACvp9I5uw87UVA4nlcvNgeGq4IcNw1UZGZk8Hq+w+BAAAHC5OT09PXOuwOvxZEK7JweB12oU9aah2+wNG5iMdcveWbB12zYGg8FisTZv2hwTE/NpRASDwYiJiQkJWQqmiYyOBiEhSzmc1AsNDc/feoqinC7nHbP1V+kpBGketlqpR49KcrgHszPz9uzRXL0ql8nEYvHRaY4dKxUHAcOwRqMBKIrO+cqCZX6/z+2evEXUP84SbtbBrL1F+3L0BoNmCpWqUTUbjUajnQJFUTCz3V6Px+/3B76Bnw/HxwONJEn76Oiw1ep4QB0/yBcLDxzJz71lMrU0TxY1BGnWzgadAsMwHMfBTPpej6f54sW7ZvNU3+v1eALiJz6f0+Vyulwuj0fXpOXvTqutlh7l5Y+7XKah2ziOG/R6DMMCQWfiBqosQRDPit15iaRCIDjOL/D7/aa+vkMZGfVyuV6n60SQoYGBerm8E0EE33Nbrl86WSmCWHFFWTnVFRUEQdju3rXYbBabjQjCbDYPDposFutTwYOxMUQ9+dz1rTcqBcLzEontzu06mRQ+cUIFn60QCOpkUkF29h3i1tAwoUWbGs/JzCZTvVwuysuvKhdfrqm9XFNrMplmbqrFYrVYrCRJPj2Dzq5O40BPe1drjxHD9Dd7+w363g59d1unoW2y392m7+1o72ppuFh7TlmtvqRWqKvrVNW/XWusrT1bJvyxKPfbEv6+QaL//v3RQMma4ekKIAhiJWzR3mxhJWxJ25vWdKNp9do133GzapR1H635uLD4UI2yLjxi3Q/7eSfKy+n05WXHy7K42QsWL5Keke7bnxv/VfzVluv3Rkb+E50kyX8BVNiATQ8WiVoAAAAASUVORK5CYII=" nextheight="418" nextwidth="659" class="image-node embed"><figcaption htmlattributes="[object Object]" class=""><em>Median (Percent 50) fees as 4.3 cents during Solace launch on virtuals at ~3pm UTC May 29, 2025.</em></figcaption></figure><p>As Base continues to grow, our scaling approach is evolving to ensure the network can sustain high traffic periods while maintaining sub-cent transaction fees. Our proactive scaling efforts have successfully handled significant surges in network activity, and we’re continuing to enhance our strategy to support the next generation of apps bringing 100x more traffic to Base.</p><p>Our new scaling strategy focuses on optimizing the fee market mechanism to effectively manage network congestion and user demand. Our scaling plan involves several key approaches:</p><ol><li><p><strong>Adjusting the elasticity ratio</strong> to control traffic flow and provide a better user experience</p></li><li><p><strong>Updating dynamic fee parameters</strong> to improve fee responsiveness</p></li><li><p><strong>Enhancing network capacity</strong> through targeted infrastructure improvements</p></li></ol><p>This approach ensures that as more people come onchain, they benefit from both affordable fees and fast onchain UX, even during peak times.</p><p>We build in the open, and want to share our learnings as we continue to scale. Here’s more on our progress so far, what’s next, and how we’ll continue to scale Base to serve as a platform for the global onchain economy.</p><h3 id="h-our-scaling-approach-thus-far-increasing-gas-target-from-25-mgass-to-35-mgass" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Our scaling approach thus far: Increasing gas target from 2.5 Mgas/s to 35 Mgas/s</h3><p>To date, we’ve increased Base's throughput by continuously raising the network’s gas target—starting 2.5 Mgas/s at launch and increasing it to 35 Mgas/s so far. Prior to scaling, we saw median fees around ~30 cents. Our scaling efforts have lowered median transaction fees &gt;10x to fractions of a cent. Increasing Base’s throughput future-proofs the network so that it can sustain even bigger spikes in traffic.</p><figure float="none" width="649px" data-type="figure" class="img-center" style="max-width: 649px;"><img src="https://storage.googleapis.com/papyrus_images/b8d57105662e2950104ee6afae6bd2af.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAYCAIAAAAUMWhjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEYUlEQVR4nI1Vv28URxSeP8CkpbIpwtrduMHZFEF3lbk0IUuDNHbFkSZorXSXisvSYJ2pwp5Dgb1WCo5dBSlCY6QoZuQGbs6VLY0rzJ4rrLkLhc9zRxJxvOjuOcNyMYSn1ejtaN775v343hBjzNLSratXv/nz778OO50PfN1u730fACi1O3uhMGICAAT+T4wxVrGW5l9BHQDiOCbkBG9kxMt/vfu+7w3l+fM9e/JVr2cxcOf+/dqHAKy86vXevOlnAQghY2NjhBDf98OhBEFQqVSUUlEUhWGolAqCQMo6IURrPQpgjImitZuLi3iRXC43OzsLAK1Wu9vttVptxpjrusVikTHm+z5jjFLq+74QwnXdUqmklHIcJ4qiiYkznK8DQLY2AwAhNpMkwX9KKSHEntBaB0EQhqExRmuNKypSymKxGAQB59zzPM75xa+9K1eK9nLHANlw0jTN5XJoZlMkhJBS4vqkXpdDQUUphWscx2jFGBup6DsAcRx7nqeUopTizss/XhJC0jQ9sQWyhoyxKIpOBkjTVCkFAIyx6+UyAOTz+W+vXQOAKIoIIZVKBQAOO53X/f7rfh8bvFKphGFoAebm5u+uRF9dvJg9eVyDILjh+z4AXLp06ebiIgBorSmdXl1dKRQKQXCD0mmbVqz8wJKQ8fFxC5DP59vtl5OTUyMEfCdFlE7bPtNaO46DISMVbOy4EkIKhQIe9n2/VCoBgOM4I5QaAFi+OI6jtd7e2cluwnFAg6pkjScmztgIXNcVQiAA9tgJRUYvjcbWk3odq2rP6fcAfHr2LOq5XO5JvY5IWM63AMaY6+Xyj7dvl0ql2QsFzO/GY/Hi4OB1v9/t9prNfQD44vx5KWU2Mkqp636eJAnmlnMOAKXS9+dmZubm5rvdwSwZAHS7PSE2OV8vFL68u7KCU9MYs/FYHHY6Su3WanG322OMVavLlqWtVjufz3ueh01B6TTeY2npFiHk1KlPsCSDSWMJpbUWYtOGhgzf3tkxxrRa7XMzM1EU2SxJKRljljeu66JVEAQ48lzXjeP4nRqkadpobGXTl+2ZycmpLEClUsE7Uko557lcztLecRwACMMQN4+Jtr2z82xvDxlkBclihh4pnV5aumUBgiBAJl6+fJkQggXAFs9e6y3RFha+yxYwK3poVi7/MDPzmfVyvVxGACklIQQb7/ZgdO/iE3TY6RzzET5O4jg+ffq0vZrv+8XiYHAiBqa31Wo/evTbiOFbon0YQCk1OTk1NzevtY7j2HGctbWfbVM0GlvN5r4xBpWsz4+NoNVqC7G5urrieR5jDKf0i4MDzMnT+jFFms39p3X5bG8PD9gaBAsLCzjcOV/H0T/UOSpSyjiOq9VlITbv1Wqcr1ery78+fCiEuFerDfdFkiS/b2xIKZMkufPTnUZjC3tyEAG6xqcKkVHnfD1NU611mqZRtKa1fnFwwPn6g18eaK2PjjpJkuBQ0VpXq8vN5v7RUQffH/QAAP8AqBMiTYo2vVQAAAAASUVORK5CYII=" nextheight="614" nextwidth="817" class="image-node embed"><figcaption htmlattributes="[object Object]" class=""><em>Median fees at ~29 cents in March, just prior to our first gas target increase.</em></figcaption></figure><figure float="none" width="603px" data-type="figure" class="img-center" style="max-width: 603px;"><img src="https://storage.googleapis.com/papyrus_images/c087f9cfc6a54eef06be5697dfc9dc06.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAYCAIAAAAUMWhjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEA0lEQVR4nKVVP2hbRxi/jB49qauWdHChUQKWh2eQtUjQqoviQYZGQyMvL4VGQ0Mg0eQUGnd6hVYODZfSyIH2qYPl0ugplMPLCUqEl9N0DkanIb5MJ4hyBltX9L7n52dZcaH9OMTp937vft+/+x46Oj768/nzjY1HUr7R+lCpgdaHE0tNA9+3hsNhuDfGIOOb1npiEzUhxFT8vEn5Roh+FAkEwJRShJAoov1zEUIY44uPBuYXN2/mcp9FHUVaH3LOKW0bY6rVKkJjSaWUPjHjC+Tz+Ql8wpRSwMxkMiEzEKC0Xa/XjTHZbDYiECSRsS5Cl4rFIuBT3RdCwAkIoU8+zfnMwWQNOOelUgkhxDkHcfillMZiH6yvrxtj3r0bqrMmpTTGVCqVWCzmC1wqFApnUhQKZLPZW7e+jMfjS0tLiUQi9LdUKlUqFdu2S6WS4zgTHRHmRwhhjPn24cNQIEjR0fGR57U8r2VZi7Ozs1/fuYMQAgHwLpfL3bt/P5PJlH1zHAdjXK1WK5UKIaTZbDqOwzkn5C+Msee1ksmF8PQgAmisfD6PEKrVan5Jr4fJtSxLSrm+/l21Wt3yjRDiuu5Pjx8LIWq1mm3bjDFCyNraGmMMnCuXy9AXp21KKU2n04SQy5c//PzGDcaYMaZWq0HIGGNCdrTWUkohBGOMcy6EIIQ4jkMI2dra+r1e9wWuQty2bQcCkMRoX6ZSKRBIp9Ou60IHFwor8BRjbNt2obACj2zbhhaQUio1gIgtazG8B7rZbP7y9KmUMmznZHJBiL7ruiGP0nY2m4Xj4vE4pRRj/NXt28YYy1oEb6Avk8kFzvn8/PyUmxx2SLFYdBwnkUicvDkG5+bmLGsxn7+ey4073XXdQmHFxz+KnmBZi5zzZHLhIgGMMUIITjcn4Orq6oMH34R/oTwvWi+uXb0Wug+0crkMBdBajwUYY5S24e4pNTDGbGw8+vjKlfBC6sh09NM45gjRT6VSy8vLlFIQAILjfD8zM4Pxk1OBv1++9LwWjOvI0tOGtp5AjkejKGiM+fU3FyG0ufnsVOCC6fivFr1TJ38PO50OXNIpNfifpoMEjkJkPE09r4Xxk3Mp+i9LqUG0bEEEUkrO+XA4PB6Nlf20nnnteDSaAKM0eCsaQRQ/kyKYBFMDN+doIajUgLGuEAI2nO9FycGo0Frv93rw2Gf3Xx8c+GOnz1h3v9d7tfdKiD58AIDG+R7QGOvCt0GIvhCi09kVor/f670+OAhq0Ghs//BjlbEupe1mswmbzc1nGP9MabvT2W00tgnZobR99+69RmOb+UYIobQNuOe1IAhCdra3/wCQkB2lBu9NUfi1gjsFM6rT2X07fBsSInkP9hBKuNda/wNQvI8E+aFDaQAAAABJRU5ErkJggg==" nextheight="622" nextwidth="829" class="image-node embed"><figcaption htmlattributes="[object Object]" class=""><em>On a busy day, median fees today are still less than a fraction of a cent.</em></figcaption></figure><p>However, with fees now so low, we’re shifting our focus to better sustain periods of high traffic with greater throughput while continuing to maintain low fees. To better understand how we’re approaching this, we must first provide some context on the fee market mechanism on Base.</p><h3 id="h-explaining-the-fee-market-mechanism-on-base" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Explaining the fee market mechanism on Base</h3><p>The transaction fees that control traffic on Base include “base fees” and “priority fees”. This can be compared to an amusement park, where the base fee is the cost to enter the park, and the priority fee is an extra amount you may choose to pay to skip a long line for a ride.</p><p>In ideal circumstances, this fast lane enables someone to get a front row seat on the ride, but causes minimal delays to the people in the standard lane. However, sometimes someone in the standard lane could end up waiting indefinitely as new people keep paying extra to skip ahead via the fast lane.</p><p>The base and priority fees are used to keep Base’s throughput close to the gas target. This is done via the EIP-1559 fee mechanism: when a block is above target, base fees rise, and when the block is below target, base fees get cheaper. Due to this, average gas used or throughput stabilizes around the gas target. <br><br>The “gas limit” dictates the upper bound capacity of a block. Today, the gas limit is 70 Mgas/s and the gas target is half of that at 35 Mgas/s. Given Base’s 2 second block times, this means the gas limit is 140 Mgas/block and target is 70 Mgas/block. This can be visualized in block explorers, where the following block used slightly above the gas target:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/d0de02ce76cef806cf7943dda8f92ef8.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAGCAIAAAAt7QuIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABOUlEQVR4nGP4CQb/cYOfUPALWSWcfffx7fU710IE//z78+ffn//////+8/vvv38QQYZJk6fU1TWERUaVlZUHBIbkFRSiWfDgwQMJCQkmZmZzc3MxcYnomJig0HC/gMDcggKQfkkGbQ+1u9fv3X36JKqiKLm+2iMztbCnzSImzDUj6c37dwzPwODGzZu3bt26dPnyrVu3MD1xDgyuXLly7/59sMrbDx48unXr9oePH5ZuXfz71+9nz559+/Hj1oMH9589vXjr+v1nT7cc2L/z6JG///4xfAYDpKBAD66fP3++ffsWogzCQJP99v0bnhBmaGvvKCsrDwuLLCwsyczOra6pRVPx/PkLYWERBhgIDQ2FmAu3AE09JPT//PsDYTBAXP38+Yu3YIDmQAiASL19+/bZs2dYFeDxAQDbXeeKlhYh7wAAAABJRU5ErkJggg==" nextheight="97" nextwidth="519" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>The gas target is essentially the ideal and sustainable number of guests in an amusement park, where everyone is still having a positive experience. The gas limit is the maximum capacity of the park.</p><h3 id="h-our-observations-from-scaling-thus-far" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Our observations from scaling thus far</h3><p>As we proactively scale Base by increasing our gas target alongside the gas limit, we have observed a few trends:</p><ul><li><p><strong>Bloated Gas Target</strong>: our current gas target (35 Mgas/s) is higher than the sustained organic traffic on Base. Consequently, we are observing higher rates of spam and inefficient resource usage. Essentially, the amusement park is not at ideal capacity - guests are paying very low fees, but it’s not a sustainable model for the park.</p></li><li><p><strong>Slow Fee Responsiveness and Reliance on Priority Fees: </strong>During traffic spikes, fees have not risen fast enough to account for this increased activity. During peak traffic, some people may submit transactions to the Base mempool, but end up waiting a long time for their transaction to get included, if it ever does. People turn to paying additional priority fees to avoid lines, and only those who pay these extra fees end up having a decent experience. Those in the standard lane pay a deceptively low ticket price to enter the park, but then get stuck in a long, stagnant line for a ride.</p></li><li><p><strong>Scaling bottlenecks: </strong>Somewhat tangential to the other observations, we acknowledge that Base is functioning around the limits of certain scaling bottlenecks, which we have <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.base.dev/scaling-base-in-2025"><u>discussed in the past</u></a>. Two specific constraints prevent us from scaling significantly at the moment:</p><ul><li><p><strong>Geth constraints: </strong>node providers sometimes report issues with Geth nodes keeping up during peaks in traffic. Our efforts to migrate to Reth, and some amount of optimizing Geth should reduce this constraint significantly.</p></li><li><p><strong>L1 DA limits: </strong>Despite the increase of L1 blobs in the recent Pectra hardfork, L1 data availability capacity is still limited, and Base can still exceed these limits at its current gas target. We are actively supporting PeerDAS efforts for the upcoming Fusaka hard fork, which will provide much more wiggle room here.</p></li></ul></li></ul><p>To understand our path forward, we are reorienting around what our core goals of scaling are: we aim to maintain low fees on Base to bring more people onchain. But simply lowering fees by increasing throughput is not the ultimate marker of success. Importantly, changes in traffic patterns should not degrade user experience or chain functionality.</p><h3 id="h-creating-a-healthier-fee-market" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Creating a healthier fee market</h3><p>We started making modifications to our fee market to create a better experience for users and ensure the chain is functioning in a stable and sustainable way.</p><h4 id="h-scaling-while-reducing-throughput" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Scaling while <em>reducing</em> throughput</h4><p>To reduce reliance on priority fees, the base fees need to better control traffic – i.e. people would be better served by paying a fair price to enter an amusement park that isn't overly crowded. If the base fee functions properly, this is like making entrance pricing more expensive during peak times like weekends and holidays, to control traffic and incentivize those with flexibility to come when it’s less busy. This model should make the number of visitors to the park more evenly distributed across days.&nbsp;</p><p>We are planning to achieve this by modifying the ratio between gas target and gas limit, also called “elasticity”. As described, this is currently a 2x elasticity ratio, with gas limit being double the gas target. Moving to a higher ratio allows base fees to better signal network congestion. This reduces the need for users to engage in speculative priority fee bidding.</p><p>As a result, users are less likely to experience delays in transaction inclusion during traffic spikes. They’ll receive more immediate feedback via an error response if their transaction has too low of a base fee, as opposed to their transaction sitting in the mempool for a long time because they did not pay enough priority fees.</p><p><strong>We’ve decided to update elasticity to 3x</strong> by <em>reducing</em> the gas target from 35 Mgas/s to 25 Mgas/s, and <em>increasing</em> the gas limit from 70 Mgas/s to 75 Mgas/s. This accomplishes a few things:</p><ul><li><p><strong>Continuing to scale</strong>: Gas <strong>limit</strong> ultimately dictates the burst capacity of Base, so this change allows Base to continue scaling</p></li><li><p><strong>Scaling safely</strong>: If we kept the gas target the same and moved to a 3x elasticity, we would need to increase the gas limit by 50% to 105 Mgas/s. By making a jump this large, we cannot ensure the health and stability of Base nodes, most of which are operated outside of the Base team.</p></li><li><p><strong>Avoiding L1 DA Limits:</strong> Reducing the gas target will ensure we do not exceed healthy L1 DA consumption levels, while this is still a scaling constraint.</p></li></ul><h4 id="h-more-responsive-fees" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">More responsive fees</h4><p>In addition to modifying elasticity, we are adjusting the rate at which fees increase via the “EIP-1559 denominator” value. This makes the base fee respond even more dynamically to account for network congestion, similar to how rideshare app prices update in real time. We have already made modifications to this parameter and seen the fee markets successfully adapting to network congestion more effectively. A higher elasticity value enables a larger range of values here.</p><p>Wallet providers should be aware of these changes to make base fees more responsive to transaction demand. Transactions should account for this variability by setting the 1559 <code>maxFeePerGas</code> field to a multiple of the latest <code>baseFee</code>. We still intend to keep fees very low (&lt;1 cent) for most use cases, to give users the best experience on Base.</p><h4 id="h-additional-explorations" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Additional explorations</h4><p>We will continue to evaluate usage of the chain, including the fee market, and explore other ideas to create a healthy ecosystem on Base. Some ideas include:</p><ul><li><p><strong>Implementing a minimum base fee</strong>: this should disincentivize spam, but also be low enough that users do not notice an impact.&nbsp;&nbsp;</p></li><li><p><strong>Adjusting calldata costs</strong> to better account for the upper bound of Ethereum L1 DA constraints.&nbsp;</p></li><li><p><strong>Repricing other opcodes</strong> to more accurately reflect execution times and therefore more predictably estimate a “fair” fee.</p></li><li><p><strong>Redesigning the mempool</strong> to better support traffic bursts and provide users more transparency and ability to modify transactions submitted to the sequencer.</p></li><li><p><strong>Explore multidimensional and localized fee markets</strong> to reduce the impact of high traffic for certain launches on everyday chain usage.</p></li></ul><p>These ideas range in complexity and tradeoffs, and we will continue to explore and analyze them based on data and observations.</p><h3 id="h-whats-next" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">What’s next</h3><p>We will soon increase the gas limit from 70 Mgas/s to 75 Mgas/s and simultaneously reduce the gas target from 35 Mgas/s to 25 Mgas/s. We will continue to monitor and track activity on Base to see how these types of changes impact network functionality. We are also working to redefine our North Star metric for scaling, which has historically been anchored on throughput – since we believe this captures only part of the full picture of our scaling goals.</p><p>We continue to scale Base, so it can serve as a platform for billions of people and millions of builders. But it’s still Day 1 - we continue to evaluate the success of our scaling efforts and iterate to maintain low fees for users, thus allowing the world to come onchain.</p><p>If you’re interested in helping us build a global economy that increases innovation, creativity, and freedom, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.base.org/jobs"><u>we’re hiring</u></a> — and we’d love to hear from you.</p><p>Follow us on social to stay up to date with the latest: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/base">X</a> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/i/lists/1876354838289932418">Base team on X</a>) | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://warpcast.com/base/">Farcaster</a> | <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://discord.com/invite/buildonbase?utm_source=dotorg&amp;utm_medium=nav">Discord</a></p>]]></content:encoded>
            <author>base-engineering-blog@newsletter.paragraph.com (Anika Raghuvanshi)</author>
            <author>base-engineering-blog@newsletter.paragraph.com (Brian Bland)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/e0e0fd61b27c3d34be4d0df0440bb810.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Engineering the Commerce Payments Protocol Powering Shopify]]></title>
            <link>https://blog.base.dev/commerce-payments-protocol</link>
            <guid>6Yx9LNFutCWhgUSP65Th</guid>
            <pubDate>Thu, 12 Jun 2025 18:26:15 GMT</pubDate>
            <description><![CDATA[Shopify and Coinbase are bringing payments onchain at scale with a new smart contract protocol to meet the complex requirements of commerce.]]></description>
            <content:encoded><![CDATA[<p>Coinbase and Shopify are launching the Commerce Payments Protocol to address a major gap in crypto: enabling real-world commerce. While most onchain payments today work well for peer-to-peer transfers, they fall short of handling the complexity of commercial purchases, which require a multi-stage payment commitment process.</p><p>This protocol brings sophisticated, multi-step payment flows onchain, while preserving crypto’s core advantages of speed, low cost, and global reach. It bridges the promise of crypto with the realities of commerce.</p><p>Available now on Base and open to all developers, this protocol powers Shopify’s new USDC payment method, which is rolling out to millions of stores using Shopify Payments. This means merchants worldwide can soon accept USDC on Base seamlessly through Shopify Payments.</p><hr><h2 id="h-why-crypto-payments-matter" class="text-3xl font-header">Why Crypto Payments Matter</h2><p>Crypto payments with Base improve the traditional payment experience and unlock entirely new opportunities. Payments settle faster, cheaper, globally and without intermediaries. Onchain payments are also programmable, carrying not just value but also instructions that can trigger other actions across the financial ecosystem.</p><h3 id="h-faster" class="text-2xl font-header">Faster</h3><p>For cashflow-conscious businesses, the difference between having money now and having money next week can be existential. For goods and services that are delivered immediately, delayed settlement puts merchants at risk of bounced payments and being at a loss. On Base, payments can land as quickly as 200 milliseconds, even across international borders, providing an unmatched merchant experience.</p><h3 id="h-cheaper" class="text-2xl font-header">Cheaper</h3><p>Transaction fees imposed by existing payment networks can detract a significant amount of merchants’ take-home profits, especially internationally. Transaction fees on Base are dramatically cheaper at typically less than $0.01 and consistently decreasing as network capacity grows. Base’s cheaper fees enable a more fluid economy where commerce can put more value in the pockets of those creating it.</p><h3 id="h-globally-accessible" class="text-2xl font-header">Globally Accessible</h3><p>Base is internet-native and does not have borders. Anyone with an internet connection can have the opportunity to transact in a global economy. This opens up new markets for merchants and access to goods and services for buyers around the world.</p><h3 id="h-composable" class="text-2xl font-header">Composable</h3><p>Existing financial infrastructure is a patchwork of different gatekeepers and standards. This increases the difficulty of building integrated systems and leads to a proliferation of different apps and accounts for users, complicating the experience for everyone. Blockchains establish a shared ledger that enables different systems to reference the same source of truth. This has led to the emergence of shared standards that simplify the integration process, empowering developers to build novel, interconnected experiences that benefit all.</p><hr><h2 id="h-commerce-the-missing-piece" class="text-3xl font-header">Commerce: The Missing Piece</h2><p>Crypto payments have been around for many years, but large-scale commerce has lagged.&nbsp;</p><p>Fifteen years ago, someone successfully paid for two pizzas with Bitcoin. Most onchain payments today still have not deviated from these direct token transfer implementations. We have been unfortunately stuck in a local maximum that works enough for basic things but not beyond.</p><p>Commerce demands more sophistication. Commercial payments involve race conditions between checkout, settlement, and fulfillment. Inventory can sell out between completing checkout and payment processing. Tax obligations can change between checkout and settlement. Buyers or merchants may want to cancel partial or full orders and refund payment.</p><p>Traditional finance addresses some of these concerns by separating buyer approval (e.g. entering card details) from the asynchronous checks a storefront performs before executing payment. The crypto analogy is letting buyers sign off on spending funds without immediately broadcasting a blockchain transaction.</p><p>After buyers approve a payment, funds must be locked while still allowing either party to cancel if needed. Traditional payments handle this with a two-step “authorize and capture” process: first locking buyer funds (authorize), then disbursing them to the merchant (capture).</p><p>This simple pattern has supported commerce complexities well before blockchains by providing:</p><ul><li><p><strong>Merchant protection </strong>through payment guarantees</p></li><li><p><strong>Buyer protection </strong>through reserved but not yet finalized payment</p></li><li><p><strong>Lower costs for merchants</strong> because they only pay fees on captured payments</p></li><li><p><strong>Custom payment finalization</strong> by capturing in parts, potentially not up to the full amount</p></li></ul><p>Until now, onchain payments have been missing this capability. To make onchain payments work for commerce at scale, we needed to design a new protocol capable of achieving the same payment properties.</p><hr><h2 id="h-escrow-architecture" class="text-3xl font-header">Escrow Architecture</h2><p>To implement authorization and capture, we introduce an escrow smart contract to sit between buyer and merchant. On authorization, funds move into the escrow from the buyer. On capture, funds move out of the escrow to the merchant. The smart contract enforces that if funds are authorized, then they are put on hold to guarantee the ability to capture at a later date. For generalization clarified later, the protocol describes a buyer as “payer” and merchant as “receiver” to state their purpose more plainly.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/663839faebf116ac1a67e98f80f94ac7.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAICAIAAAAX52r4AAAACXBIWXMAABYlAAAWJQFJUiTwAAABFUlEQVR4nK1SQbXEMAjse3Ww95577z13BCAAAQiogRiogRjAQBSgAAVRgIL+t+X9bLa97lySFsLADNP5j9ZarVVVz/N09/MbZqYDzOyWEE8i2lrr/6c4RAQRmZmImDmy/UI8SylFNBKWZRnrxoWZowgAiMiHwMxSSp0258zMY3e11n3fx3JEdJuSmXPOXQwAiCnfBDlnEXH3lFIwI6KqAgARlVKC0t37fIjYWiulAEApJVpUVUQkIlU9LnwRvD+mKboAgOM4Xq8XAHRl3H270FpDxForM8/zzMwisq6rmS0XRKSU8iGIiX4okbt/SRQmhyDh0tPkbdv6KEQ0mtyTw2QiupvcnbELzx0dox3PnNgXMxvX9A8qgTsqbYgD/gAAAABJRU5ErkJggg==" nextheight="500" nextwidth="2000" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>An escrow architecture also allows us to make no assumptions about the payer or receiver addresses and their capabilities. For example an alternative construction would be to introduce a locking mechanism on the payer’s wallet, but this comes with many assumptions about the payer that reduce compatibility.&nbsp;</p><p>Anyone can use the escrow smart contract and no one can turn it off or change its code. This provides best-in-class guarantees for prospective integrators and users. It is meant to be shared for all payments across the ecosystem and is a hub for commerce.</p><hr><h2 id="h-operators" class="text-3xl font-header">Operators</h2><p>Like normal payments, blockchain transactions require a fee to execute. While the status quo in crypto expects payers to incur this fee, traditional commerce places this burden on the receiver. A fee-free payer experience is important for conversion, but introduces a new problem for receivers who would have to manage blockchain transactions to authorize their payments. To resolve this friction, we introduce a new role called an operator to facilitate movement of funds between the transacting parties.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/ac338c7bfebc15198789ebc8df9c213d.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAQCAIAAAD4YuoOAAAACXBIWXMAABYlAAAWJQFJUiTwAAABqElEQVR4nK1Uy3HFIAxkxnX47Lvv3FUABVAABdCAGqABNaAGqIAKqEAVqAJnHnohjt8kcTLZiwELrT4r3PEOZo4xIqKIHJ+hqjnn9I4YY0rpYnMch4ggYoyRmeehs49d670Tkfe+tWZ+VXVyi0ittbUmIt57i2PatNa890TUez9H8CCotYYQzHq6O4dWa805T4PjOGKMc20IIdRa5zalZNsHQUrJ4nLOGfOyLIi477udxIGHtXPLsqiq976UAgDOuXVdmXld11qrG0DEUgoiPgmY2QIkIhkAABFprTGziBCRGfCAZdAHmLnWqqoA0HuvA9YzInoSqGoIgYhUVURs/X2JQgiXEhFRCEFEVJWIAOBTk0VkluKsgfNfRASAfd8R0Xp2gTXPlDal+CQwzKAu0fXecSDGuG0bIuace++vHPPu9PBBYIKbsjsDEZdlCSFs2+acAwCj+dH7NYPvwaMCJsfXIL7CXYKUUs7Z8rMq/ydBH8MpIiZNE+XNJG4R8BiUUorNkU3+65P1dwIdg3LW2D9nMOfIBhUASik3L/5CRa01e7Ttrb1J8AZ0qfJkFsgJyAAAAABJRU5ErkJggg==" nextheight="1000" nextwidth="2000" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Similar to payers and receivers, the protocol is not concerned with how the operator is implemented and just recognizes it as an address. Operators could be simple externally-owned accounts or advanced smart contracts. Operators could also be controlled via API to easily integrate with existing commerce workflows. Coinbase provides such a service and the protocol is open for anyone to build their own as well.</p><p>While operators exist for payer and receiver convenience, this benefit should not supersede security. Users should not have to trust any operator or managing entity to be safe. We have invested in multiple efforts to limit the controls given to operators to stay true to this crypto ethos.</p><p>First, operators cannot modify the original payment intent of the payer. This includes things like the receiver address, token of payment, maximum amount to be paid, and expiries for authorization and other operations. We mitigate the ability for operators to mutate any of these fields by having the payer sign over a hash of these details. By hashing this information, we probabilistically guarantee that any change to any field will result in a new hash easily identifiable as different from the original. By signing over this hash, we guarantee that a given set of payment information is aligned with the payer’s intent. Additionally, the protocol rejects attempts at using a signed payment more than once.</p><p>Second, operators cannot lock funds in the escrow. If the authorization expires and the operator has not yet captured or voided the payment, the payer is able to reclaim funds directly from the escrow without operator intervention. This hopefully never needs to happen as this mitigation and lack of incentive is not clearly worth a potentially malicious operator’s time.</p><p>Third, operators cannot impact the activities of another operator. Given anyone can become an operator, this is important to protect all honest actors even if some begin to act maliciously. The operator address is encoded into the payment information sealed by the payer in the signing process. This guarantees that only one operator can manage the payment through its lifecycle. Additionally, the payments managed by different operators are escrowed in different buckets. This measure proactively reduces the risk of a theoretical vulnerability being discovered in the future. You can read more about our security preparation <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/base/commerce-payments/blob/main/docs/Security.md"><u>here</u></a>.</p><hr><h2 id="h-payment-operations" class="text-3xl font-header">Payment Operations</h2><p>The protocol implements six core operations that cover the complete payment lifecycle:</p><ol><li><p><code>authorize</code>: transfer funds from payer to escrow</p></li><li><p><code>capture</code>: transfer funds from escrow to receiver</p></li><li><p><code>charge</code>: transfer funds from payer to escrow to receiver (1-step authorization + capture)</p></li><li><p><code>void</code>: return of uncaptured funds from escrow to payer</p></li><li><p><code>reclaim</code>: payer-initiated void, only available after authorization expires</p></li><li><p><code>refund</code>: return of captured funds to payer<br></p></li></ol><p>Each operation updates payment accounting in the protocol, tracking authorized payments available for capture and captured payments available for refund. The protocol enforces that payment transitions only occur in the valid time window defined by the payment’s various expiries.</p><h3 id="h-authorize" class="text-2xl font-header">Authorize</h3><p>Reserve payer funds in escrow for future capture, enabling delayed settlement and guaranteeing merchant payment. The protocol leaves collecting funds from payers open-ended and extensible, but our initial implementation integrates with the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-3009"><u>ERC-3009</u></a> standard. This standard allows payers of all wallet types to grant permission to spend funds with just a signature, deferring transactions to operators. The protocol enforces that “pre-approvals” like this signature must be used to authorize payment before an expiry.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/5ceb88644412f9bbd3759539818c3d56.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAQCAIAAAD4YuoOAAAACXBIWXMAABYlAAAWJQFJUiTwAAABiUlEQVR4nK2UTZUDIQzH0dB7TfTOPQIQgAAEYCAGMBADMYACFKAgCqKAfSUtOzuzO6+v2/9lPgj5JSTBjaeYOcaIiCIyfkpVc87pqRhjSmlnM8YQEUSMMTLz+unsYdt670TkvW+tmV9VXWwRqbW21kQEAGxpqbXmvSei3vs2gjug1hpCMI/L3XZzrTXnvAzGGDHGHSCEUGtdnykl+7wDUkoWl3POyN77Usr1enXO5Zzj1N3aucvlIiK/ApjZTSFiKQURHwBmtgCJSKZCCKpaa2VmESEiM+CpvzLovdcpqxkRPQCqGkIgIlU177Z2ckRhRrC1IaIQgoioKhEBwI8ii4jVedcDJqMiIgDcbjd7GQdZ8azTVis+AKYV1C66Mf8wMwB47+0QjoDt3mXwDbCmXK153IOIVuRSyove9xmcSGcGMUYAYOaTDHZ6FZBSyjlbfvb+SUCfwykifUpVj5P8LwDPQSml2BzZ5B+vrPcBOgdl22MfzmDNkQ0qAJw00psAuy/t0ra79kXAF1QlDvxptS6/AAAAAElFTkSuQmCC" nextheight="1000" nextwidth="2000" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-capture" class="text-2xl font-header">Capture</h3><p>Transfer authorized funds from escrow to receivers to finalize payment. Captures can be made in multiple partial increments to support merchants that may fulfill goods in multiple deliveries. The protocol enforces that the total captured amount cannot exceed the authorized amount. Captures must be made before the authorization expires, defined at the time of payment signing.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/3be548ada059f06630951528fde4605d.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAQCAIAAAD4YuoOAAAACXBIWXMAABYlAAAWJQFJUiTwAAABkElEQVR4nK2UTbEsIQyF0dD7Wc9+9uwjAAEIQAAGMICBGIgBFKAgCqIgCnh1yVxeX7qqa96r+236L3ASctJufENEMcZSioiMn6hqzjl9E2NMKW0xYwwRKaXEGIlovXR2sWXMjIje+977eWVrLcYoIn0iIgCgJ8YYvXfvPSIy8zmDL4HWWgjBMl2lbAI55xUwxogxrnsjhNBaW48pJXv8EkgpWV7OOVMOITDzWaCUoqrOueM4VNW28947516vFxE9Ho/WmpuUUmqtpZS3ABFZgogoEytoExgz0s53nRgR9d5VFQCYuU2sZ4j4FrCMEFFVbXf7dnNEIYTtiBAxhCAiqoqIAPCjySJifd48YIhIjDHnDADP59NuxgVrnjltWfEtYKyktuzGfMPMMGHma8C2yQr4K7DZ7opMFxzHsZn4Zve9gntqrSklACCimwo2PhVIKeWcrYd58psCPIdTRHhipvywiI8EaA5KrdXmyCb/+sv6fwGdg3L22C9XsObIBhUAaq0fLvwHF/Xe7ad9Y9OrwB9IEBF196ap1wAAAABJRU5ErkJggg==" nextheight="1000" nextwidth="2000" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-charge" class="text-2xl font-header">Charge</h3><p>Combines authorization and capture into a single transaction to improve efficiency for payments that do not require delayed captures. Follows the same flexible payment collection and pre-approval expiry patterns as authorize. This is analogous to the “sale” credit card transaction type.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/7c545821498c11743380cc2efd775c08.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAQCAIAAAD4YuoOAAAACXBIWXMAABYlAAAWJQFJUiTwAAABmklEQVR4nK1UQbHrMAz0THGERO6+B4ABGIABiEAImIAJiIAQCIERCIEQ+M/Ltm5f087r/OleksiOdmWtHMYNzJxzJiIzG7/h7kRUbsg5l1Ke9owxzIyIcs7MPIMBD/zZe2fmGKOqIq+7T24zExFVNbNt27A696hqjJGZe+/IdicQkZQSMuIz5/woTUSIaG4YY+Sc5/uMQNZUjDp+CEopImJmIQQwxxhrrcuyhBBQNShDCJfLxd1TSiISYwwhrOvKzMuyiEg4sO97rXXf9ysBMyMvM5tZ7z2l5O4igkhrDRWoqohAr5mpKjOrqrtv29Z7lwPoWWvtSgBFrbWZ/bFL43Zo7o4NZhZjfDoiZgaHmTEzzvzeZBgADnnKPo5VGCyltK4rEW3bdnYRdKDD04pXgrMvX8bp4Hi59C7DneDJducf3B1tN7N3Cs4ZXlfwErXWnHNKqdb6juCMTwlKKUQEdejWNwl673CRHoApPyziIwJmJqJaK+YIk3++sv6fwI9BmZK/X8EYo7WGAcRAYUq/SYB7gg48Xmp/EvwDJrAF3vfnGLkAAAAASUVORK5CYII=" nextheight="1000" nextwidth="2000" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-void" class="text-2xl font-header">Void</h3><p>Cancel an authorized payment, returning escrowed funds back to the payer. Used to cancel transactions for errors potentially unrelated to payment or if merchants are no longer able to fulfill the order. Can be made at any time but only once per payment, reversing the entire remaining authorized amount.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/8f0cd3be85e495eed6ea886ce4b320e4.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAQCAIAAAD4YuoOAAAACXBIWXMAABYlAAAWJQFJUiTwAAABfklEQVR4nK2UXXEFIQyFeVohCMAABqIABSiIAxzgIA6QgAQkREIs0Cnnltvu7rSd9p6XXf7yBTjBzQ+JCBHlnFV1fpWZ5ZzTh4gopXSaM+dU1ZwzEYnI7nT4YOUYQ0S89601xDWzzVbV1lrvXVVDCBja6r1770VkjIFoT0BrLcaIiGgS0efFrbWc854w5ySiE4CIeu+7mVLCPt4BKaXWmqo650B2zjGz9945R0TMnHM2M7ekqldAjFFEMIGZSynM/ACICOKKiC7hBHrv6BER7KD3jtO7BYwx2hLurNb6AJhZjLHWqqpjDORyOiJaETFBVb33J4CIhBAwKiI48+clwwBwyCn6XKMwWAjhOA78XF2EPHDD24oPwEmn7LZqrbcG/SbCEwBTbmveLogxHsfxTQbXCPc7uBUzxxi996WU36/6LSClxMzIDrf1SsAYAy7qS2Z2reR/AWTVQSkFdYTKvz5ZfwfYKpSd8ut3AIOiAFFQqNJXAvBO5KXPj9qPgDc6tvWLDmHvSQAAAABJRU5ErkJggg==" nextheight="1000" nextwidth="2000" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-reclaim" class="text-2xl font-header">Reclaim</h3><p>Effectively a payer-initiated void where payers retrieve their own funds from the escrow. Can only be performed if an authorization has expired and serves primarily as a safety mechanism against malfunctioning operators. This is the only payment operation not controlled by the operator.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/5e4f821480b23f8108a8d978461cdc15.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAQCAIAAAD4YuoOAAAACXBIWXMAABYlAAAWJQFJUiTwAAABl0lEQVR4nK1UUbEkIQzExf4jAAMYiIIoiII4wAEO4gAJSEBCJGCBq5veZfdmXr199W77Z2ogpEnSTVgPmBkRiYi7r38x5xQRfoCImPkUs9ZydxEhIjPbiwEfnBxjmFmMsbWGvHPOze3urbXeu7unlLC10XuPMZrZGAPZngSttZwzMuKXiF4Pt9ZEZAestYjoREBEvff9y8yo4y8BM7fW3D2EAOYQgqrGGEMIRKSqIjLnDAfc/UqQczYzBKhqKUVV7wRmhrxm5gfQgd47VswMFbQDX1aQcx5jIAAzq7XeCeacOedaq7v33nGXU4voyOjuYwx3jzGeCMwspYRdM0PPn0OGAG63G9p1lQczq2pKKYQgIvv89R6Y8JbinQBQVfSkH1DVnHMpZRxwdwSsH2DX9yRAB2qtGFEpBTUR0V7cOjn15zXJFvcXFXyPUgoz55whjx/iDcF8XIeZodS1Flz9GQJgjAEVYTZzzquT/4vADh+UUuAjOP/6ZP2eYB5G2Vf+fAVrrVorDAhDwaWfJMB7KQdeH7W3BH8AdXneVFw2HXoAAAAASUVORK5CYII=" nextheight="1000" nextwidth="2000" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-refund" class="text-2xl font-header">Refund</h3><p>Return funds to payers after payment has been captured to the receiver. The protocol leaves collecting funds for payers open-ended and extensible, but our initial implementation integrates with the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-3009"><u>ERC-3009</u></a> standard. We use this standard to add flexibility to withdraw capital from multiple potential sources such as the receiver, another merchant-controlled address, or the operator. Refunds must be made before an expiry after which captured payments are considered final.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a89298b98ed5a5496c5bda711509cd4d.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAQCAIAAAD4YuoOAAAACXBIWXMAABYlAAAWJQFJUiTwAAABk0lEQVR4nK1UwZEjIQykaoLx33/+BEAACoAAlAAJkAAJKAEiIAIiUASKgK2jbeyzZ2vvttyfGYRQS6iFm3eICBExs6rOv2FmzJzuIKKU0ovPnFNVmZmIRGQbHT44OcYQEe99aw1xzWxzq2prrfeuqt575LF9eu/eexEZYyDag6C1FmOEN5ZE9Jxaa42Zt8Ock4j2/7b03vcypYQ6/hCklFprquqcA/NxHDln771zjohyzsxsZs654zjMzHtfSiEiWHb6biHnXErJOd8IRARxRUQXQghm1nuHRURQQe8dt0dEY0FEYAkhjDHaAnpWa70RmFmMsdaqqmOMGONzl+b90swMDujByxWJCDiQEO780WQIAAp5iT7XLgQWY7xer8wcQnhXEfJAh7cUbwTvujy18+L4bvc0woMAgtuyOz1wuVycc6r6Hcd7hPMKTlGWbEIItdZ/KeL/CFJKUOqe6k8SjDGgor5gZtDxxwhkzUEpBXOEyX9/sn5PYGtQdsqfr2DOWWvFAGKgMKWfJMA7wQvPj9qPBF/X0QWHW2igBwAAAABJRU5ErkJggg==" nextheight="1000" nextwidth="2000" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><hr><h2 id="h-example-payment-flow" class="text-3xl font-header">Example Payment Flow</h2><p>To start paying with crypto, a merchant storefront requests a buyer to sign payment information with their wallet. The merchant storefront requests payment authorization from an operator service, providing the signed payment information. The operator service submits a blockchain transaction, directing the operator to call the escrow smart contract to authorize the provided payment. On successful execution, the buyer is given confirmation that they have paid. At a later point in time, the merchant can capture or void this payment.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/6dcae11ca0988c62820b7459f85ffe29.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAdCAIAAABE/PnQAAAACXBIWXMAAAsTAAALEwEAmpwYAAADLUlEQVR4nKVWIZisIBCmXjQZLm25YrJssphIJJOJZLKZTJtsNBONZDPZbDQajWaz0Wg23nfOPt6eu/vevb0/+AnM8M8MDDPIe78si9baWmuMWZZlXVdjjHNu2QFD+LE7jDHrjmVZrLXLsmzbFmTC0rqu3nvkvVdKYYzLsqzruizLoiiEEM45a60QghBS13VRFNZa4GCMYYybpgEVrbVzTilFCCnLsmmaoigYY9baK0EgBANhGIwyxmitg5fBUaWU+Y0grLVWSgXhK4H3/nK5MMYQQoSQqqqmaYJ57z3Eyt9hWRbn3GHyXvhKIKX8HOxgjDnnth3e+23bnhGAwC3uha8EAKWUlBJc+4vO6wR5nvd9n2VZHMdJkkRRVJblMAzg348IQBQhhDFWSk3TNI4jfJummef5PtyveHCvs67r+/s7QohSelh6hQDy66AjhEAIVVV1OJsXPQACKaUQQikFOpCTB7xCMI4jY8wYQynNsqxpmm3b0jRF6FPs4NwrBEIIY0wYaq37vieERFHEOR/H8S/xBDLn3O0OD27R6XSapokxxjmv6zrLMoxxnufn85lSyjnv+55zfrlc6rq+j9vTTH5o1DOdbbcmSZKmac7n88eOKIoIIUKIQ9L845r6R14HdwkhWmvIGCEEOD0MAzyR3yXYvvdUUEo/Pj7iOEYIJUkSx7HW+kcE5nc8QWWaJqUUZGWapl3XfXmuf+4BwFrbdZ2UMiw9TrRbuCf1AApO27aU0rqu27atqmocx67rglv/58H2dUlKyTkfhqHveyHEMAzzPJ9Op0NWfiGYd0BRdM71fZ+maZZlSZJkWZbnObQBUOsRQvM8P7PmQaIZYxhjUMShLIcyO88zDGE+ENxW1mfxvBb9tm3hAry9vRVFcWva9vOKZq0dhgGMQgi1bXsrtP3rmn43RN57eGqCBNR9ux/J7SHDjzHGWnvbGzwUvoYIwg3XFIZwKusO+IEdYYtDfxeEw1GBzJ/OLsuyoigopYQQjDHnHDo7zjnGmFKKMYYuEVIpz3No96qqgs5OSpnnObSBGONjZ6e1Dm0atHLbtsFkWIJm7V4Y3L3fAQh+AQOrKj0zPZkOAAAAAElFTkSuQmCC" nextheight="3532" nextwidth="3840" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><hr><h2 id="h-build-with-us" class="text-3xl font-header">Build With Us</h2><p>Coinbase and Shopify are stewards of the protocol and its initial participants, but the protocol is open. Anyone can build on it and no one can shut it off.</p><p>The core of the protocol is radically simple with only 6 payment operations. This helped us reach confidence in its security, but also left open the potential to build new features on top of this foundation.</p><p>To call out a few design directions we are excited about:</p><ol><li><p><strong>Operator smart contracts.</strong> As the drivers of money movement, composing payment with additional smart contract systems is powerful at the operator layer. It is also possible here to peel back some of the control given to 3rd parties and delegate operations to other roles or back to payers and receivers.</p></li><li><p><strong>Receiver smart contracts.</strong> Layer in revenue splits, currency swaps, privacy systems, and more.</p></li><li><p><strong>Token Collector smart contracts.</strong> Our exact mechanics for collecting tokens for payment and refunds are under-discussed thus far for brevity. We originally implemented an integration with <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-3009"><u>ERC-3009</u></a> to support gas-less payments with USDC, but many other opportunities exist for signing payment authorization. This pattern also extends to refunds where liquidity to make a payer whole can come from any location, allowing more flexible resolution between operator and merchant. Opportunities for discounts, chain abstraction, and paying with any token likely exist here.</p></li></ol><p>For a complete breakdown of the protocol, find the full implementation and documentation <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://github.com/base/commerce-payments"><u>here</u></a>. You can also reach out with questions in the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://discord.com/invite/buildonbase"><u>Base Discord server</u></a>.<br></p><p>The future of commerce is onchain, permissionless, and decentralized. The infrastructure is ready. Now it's time to build.</p>]]></content:encoded>
            <author>base-engineering-blog@newsletter.paragraph.com (Conner Swenberg)</author>
            <author>base-engineering-blog@newsletter.paragraph.com (Simon Lacoursière)</author>
            <author>base-engineering-blog@newsletter.paragraph.com (Amie Corso)</author>
            <category>wallets</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/99ca41faeec295064d906534f5f7d50b.jpg" length="0" type="image/jpg"/>
        </item>
    </channel>
</rss>