🔒🖼️ New post: Encrypted Frame Transactions 🔒🖼️
tl;dr: Encrypted frame transactions build on LUCID and EIP-8141 to hide execution parameters (target, calldata, amounts) until after the block's ordering is locked. This design unlocks same-slot encrypted execution, interleaved plain text/encrypted transactions, and is future-compatible with PQ schemes.
👇🧵
So blob-gas...
Blob-gas is a different kind of gas, that is accounted in parallel to regular gas.
Each blob within a transaction has a fixed cost of 131,072 blob-gas. We multiply this fixed cost by the number of blobs within each transactions of the block to determine the total blob-gas amount.
total_blob_gas = 0
for tx in block:
for blob in tx:
total_blob_gas += 131072
Now that we know about the amount of blob-gas, what about the price of blob-gas?
A new price is calculated at each block. Because you can never be sure in which block your tx will land, you can set a maxFeePerBlobGas in your tx to guarentee a max cost of your tx.
Each block only allows a maximum of 15 blobs.
Either 15 blobs in a single tx, 1 blob in 15 txs, or whatever.
Even though the max limit is 15 blobs, the real target 10 is blobs/block. The current block's blob-gas price will increase if the parent block has exceeded that target. The formula is a bit complicated, but the price increases exponentialy in relation to the excess blob-gas above the target.
All the Ether spent on blob-gas is directly burned and non goes toward validators.
This was introduced by EIP-4844 during the Cancun update, the limit was initialy set 6 and the target to 3 blobs/block, but it was later increased multiple times. On Jan 2, 2026, a mini-update is schedule to increase the limit to 21 blobs and the target to 14 blobs per blocks.
That's it for today! Let me know if you want me to explain other topics or EIPs!
The Solidity function dispatcher is a tree where:
- Internal nodes perform binary splits.
- Leaf nodes contain up to 4 function selectors, tested linearly.
Protip 1: the `fallback` function's bytecode is generated twice in the bytecode. To reduce bytecode size, wrap the fallback logic in an internal function.
Protip 2: if you have a very frequently used function, make an alias with a `0x00000000` function selector, which makes it the cheapest to lookup.
> Increasing Blocksize increases centralization, again, diminishing its role as a more perfect form of money. (Read, “Blocksize Wars”)
Yes, one should read "The Blocksize War".
The OP_RETURN size limit has been 1MB for well over a decade. A recent version of Bitcoin Core has increased the OP_RETURN relay policy filter limit, not the actual size limit. This recent change is not a blocksize limit increase.
In Chapter 2 of "The Blocksize War" it explains one of the key reasons why we need a blocksize limit, because otherwise the chain will be full of loads of spam or as the book puts it "music collection or encrypted documents".
The small blockers won the war. We have a reasonable blocksize limit in place. This protects us from the spam. Filters are not effective at stopping transactions people want to make. Only a tiny minority of nodes need to ignore the filters and then the filters wont work at all. We should not fight an unwinnable war. And to the extent the filters do work at reducing the propagation of transactions people want to make, that causes centralization pressure, pretty much the same kind of centralization pressure the small blockers fought to stop in the blocksize war.
Filters are not a robust long term solution to fighting against mining incentives or fighting against spam. Bitcon should not rely on ineffective measures like filters. The blocksize limit is that robust long term solution.
Chapter 2 Extract
> Small blockers also believed full blocks were inevitable anyway. After all, if blockspace was available, why not use it up? Anyone could store anything they liked in the blockchain, for instance their music collection or encrypted documents. Demand for cheap, highly-replicated storage was essentially unbounded, they argued. Asking for the limit to increase above expected demand was therefore nonsense. Indeed, one person could easily fill all the space up themselves. The retort to this point from the larger blockers circled back to the mining incentive argument; miners would not do this, they claimed, miners would not let this amount of data in the blocks.
guys, we need your feedback. The `GAS2ETH` opcode (the EIP is written by @big_tech_sux and myself) is proposed for inclusion for Glamsterdam and we need further feedback about potential ambiguities in the specs, security issues, or any other concerns. The tldr for the EIP is that the new opcode `GAS2ETH` enables the direct conversion of gas into ETH which means it provides a new _native_ (i.e. L1-enshrined) monetisation mechanism for contract authors and public goods projects that scales with network usage.
Solidity memory lesson 9 (for Fantasy top fans):
Implicit memory allocation of dynamic vs static arrays.
- Dynamic array is initialized to 0x60, pointing to the zero pointer. No memory is allocated.
- Static array is initialized by setting it to the location of newly allocated and zeroized memory.
Many have read way too much into Adam's post, so let me clarify a few things.
Bitcoin Core recently announced that 5 low-severity vulnerabilities fixed in the recently released 30.0 version will be disclosed in two weeks, in accordance with our disclosure policy.
According to the Bitcoin Core security disclosure policy, severe vulnerabilities (medium and high severity levels) are to be disclosed roughly a year after a version of the software containing the fix was released. Whether Bitcoin Core version 30.0 contains severe vulnerability fixes, and whether those were backported, will therefore only be revealed a year from now. Speculating as of today is pointless.
When possible, Bitcoin Core will backport security fixes in "point releases" of previous versions. For instance, most of the fixes in 30.0 have been backported to 29.2. This may not always be possible, because it risks giving away the issue before a fixed version is available to users or before they had time to upgrade. When considering how to fix security bugs, Bitcoin Core faces a tradeoff between optimizing the defense of users against regular-contributors attacks (this is done among other things by working in public, publishing fully reproducible binaries for releases, and a firm stance against auto-updates), and the defense of users against external attackers. This post from one of the Bitcoin Core maintainers goes into more details about this: https://t.co/4VoyARwXZC.
This is why Bitcoin Core contributors usually advise to run the latest version if you can: it may contain fixes for not-yet-disclosed security vulnerabilities that were unreasonable to backport to previous versions. Failing that, for instance if there was a controversy around one of the changes in the latest major version and you want to take time to reconsider upgrading, make sure to upgrade to "point releases" as they will usually contain most of the fixes (though we can't guarantee they will always contain all of them). For instance if you are running 29.0 (or lower) and are ambivalent about upgrading to 30.0, do consider upgrading to 29.2 or 28.3.
Of course you should never blindly upgrade. Bitcoin Core, among other things, contains the definition of your money (the consensus rules). Therefore as a user it's always a tradeoff between the risk of upgrading changing the definition of your money due to a bug (or a stealth attack by devs!), and the risk of being exposed to security vulnerabilities present in older versions of the software. How you weigh this tradeoff will depend on your own usage of the software. For instance if you run a Lightning node, you may want to upgrade more aggressively because a remote crasher in Core may put your Lightning funds at risk. If you don't, it may be more reasonable to delay upgrading.
There is no one-size-fits-all when it comes to security, you need to make your own assessment for your own threat model. If you are unable to do so, or if in doubt, my own advice is the following. Don't rush to upgrade but do keep up with the latest version. Maybe with a couple months lag. If not possible, do upgrade to latest point releases pro-actively. In all cases, keep yourself posted about the latest security announcements from Bitcoin Core. Those are posted on our website, on the bitcoindev mailing list, and on the @bitcoincoreorg account here on X.
Anon take a break from the memes!
I looked at the @Uniswap V4 codebase and found some really interesting things.
If you want to know how V4 is implemented this is for you!
1) V4 uses a singleton architecture. Which means there is no factory contract anymore. All of it lives in PoolManager.sol
The pool manager is an ERC-6909 contract which gives you the ability to manage multiple tokens in one contract.
To create a new pool you call the initialize function. No need to have a separate deployment for each pool. This saves a lot of gas.
2) There are a couple of custom types. Currency, BalanceDelta. Which uses the custom operator overload feature introduced in Solidity 0.8.19.
This allows you to redefine addition, subtraction and other operators.
I think this is very nice and I should use this more often. It makes the code much easier to read and improves type safety.
3) They use this pattern for reverts, which I've never seen before:
customError.selector.revertWith()
For readability its debatable. The big upside is that you can use custom errors and still have nice error messages. I think this is the main reason they are using this but I'm not 100% sure.
The library in CustomRevert.sol makes this pattern possible.
4) In contrast to V3 or V2 you can have custom fees or even dynamic fees.
When you call the initialize function you can pass the fees that you want in the PoolKey struct.
You are not forced to use the 1/5/30/100 basis points anymore. Leaving fees up to the market is a big improvement.
5) The repos use of external libraries is interesting. The 6909 implementation for example is from solmate. But they copy the file over from a specific commit instead of importing it from an external library.
I've seen several projects do this and I think the main reason here is security. This gives you this added confidence that you are 100% sure you are using the version you intend of using. Auditing becomes easier as well.
6) Hooks are super cool!. This is the most interesting feature of V4. After most operations in the Pool there are hooks that you can use to call custom logic.
Here are some of them:
- beforeAddLiquidity
- afterAddLiquidity
- beforeRemoveLiquidity
- afterRemoveLiquidity
- beforeSwap
- afterSwap
It will be interesting to see how this plays out once it goes live. You will have some added trust assumptions about each hooks security. I expect to see here some hacks and exploits tbh but lets hope for the best. I also expect to see auditors that are specialized on V4 hooks.
One added consideration here is liquidity fragmentation as well. In V3 you only could
have one Pool for a token pair at each fee tier. Now you can have infinite pools for each token pair.
This makes routing even more important.
7) The unlock mechanism in the pool manager which you have to call first uses a transient load and store to save gas.
Something introduced in the Istanbul hard fork. In this solidity version it is still not natively supported so they use assembly in the Lock Library to call it.
8) Now you have native ETH support. Nice to have. Will save some gas on native ETH swaps.
9) I'm excited for the V4 launch. You can build so many new things using the hooks system. I think V4 will establish hooks to be a core feature for most protocol going forward.
🚨 Exciting news! Uniswap V4 is set to deploy within a week! 🚀
Now is the best time to ensure your hooks are secure and ready. ✅
Use our comprehensive checklist to test your hooks thoroughly before the launch. 👇 Don't miss out!
How ✨I found a critical vulnerability✨ in @zora's ERC20Z contract via a little known Uniswap v3/v4 property
When Zora put out this article: https://t.co/pyEQuVtXjD outlining their new protocol, I was intrigued and had to learn more
From a high level, the system works by allowing creators to sell NFT's where a portion of the revenue from the sale is taken and placed in a Uniswap v3 pool along with an ERC20 wrapped version of the NFT, instantly creating a liquid secondary market. Pretty cool mechanism. I had to dig deeper
Reading the contract, I quickly spotted something that set off alarm bells in my head. When minting liquidity, the amount0Min and amount1Min parameters were 0. Looks like a classic sandwich attack vulnerability, was this too good to be true? (spoiler: kinda)
I quickly wrote up a (messy) PoC realizing that I was looking at a pretty good payday for the little amount of time I'd spent on this. The PoC worked by frontrunning the liquidity mint to provide a small amount of liquidity to the pool and swap the token price to the maximum price, then backrunning the mint by selling the token into the newly placed liquidity, draining the position of its ETH and dropping the token price to near zero
I sent the PoC off to Zora's security team expecting the best, but alas they pointed out a significant flaw in the PoC. I dealt ERC20Z tokens to the contract to provide liquidity so that I could make the frontrun swap, but Zora had designed the system with this in mind, making it impossible for anyone to get the ERC20 token before liquidity was placed
Feeling dejected, I played around with the PoC to see if there was any way I could still make the attack possible. What if I try swapping with no liquidity in the pool? I run the updated test. I see green. It worked! It turns out that you can freely manipulate the price of Uniswap v3/v4 pool by swapping zero amounts when there's no liquidity in the way. This was exactly what I needed for the exploit
Zora acknowledged that this attack was indeed possible, patching the issue and ultimately awarding me a bounty of 11k USDC
To security researchers and smart contract developers: make sure to prevent price manipulation by using safe amount0Min/amount1Min parameters and beware of 0 amount swaps!
Shameless plug: this is the third high+ severity confirmed bounty I've reported on a protocol which leverages Uniswap v3, so if you'd like to get coverage on your Uniswap v3/v4 adjacent protocol, my DM's are open! And if you'd like to book me on a team audit with the best of the best, you can book me through @SpearbitDAO
New blog post is up:
Computing the Real Reserves Between Two Prices in Uniswap V3
We've spent most of the last 12 chapters establishing the math foundations of Uniswap V3.
We're ready to start applying this knowledge to the codebase.
Uniswap V3 code is extremely easy to read if you know mathematically what it is trying to do.
Some nice interactive tools help too!
this is what production level gas optimization in Solidity looks like.
storing multiple variables in bits in a single slot.
ex:
instead of using bool which would take 1 byte (8 bit), they allocated a specific bit [56] in the single slot (32 bytes = 256 bits) for the bool value.
JS Runtime Explained: Call Stack, Web APIs, & The Event Loop
How single-threaded JS handles async operations without freezing your browser.
→ The V8 Engine parses code and runs Synchronous tasks on the Call Stack (LIFO). It’s fast, but it can only do one thing at a time.
→ Asynchronous operations (like setTimeout(), fetch, DOM events) are immediately offloaded to Web APIs (provided by the browser/Node). This prevents blocking.
→ When an async task is complete, its callback is pushed to a Queue.
✓The Microtask Queue (.then(), await) has HIGH PRIORITY.
✓The Callback Queue (setTimeout, I/O) has standard priority.
→ The Event Loop is the tireless coordinator. It constantly checks: Is the Call Stack empty?
→ If the stack is empty, the Event Loop pushes all pending Microtasks, and then the next item from the Callback Queue, onto the Call Stack for execution.
Master this core mechanism to truly understand non-blocking JS and write high-performance apps!
Grab this JavaScript Mastery with Projects Ebook :
→ https://t.co/AzyazIHLjT
🔐 EIP-4337 Security Tip
Always validate that the `userOp` and its `userOpHash` are consistent **and** that the sender is legitimate.
⚠️ Skipping these checks allows an attacker to craft a valid hash but attach a malicious `userOp`, or spoof the sender address.
Example from our EIP-4337 audit 👇
Does not hash the `userOp` and uses the hash to know the wallet that authorized the action. It uses `userOpHash` to derive the address that signed for that hash, and uses the data `userOp` to modify it.
As we can see, the `sessionKeySigner` is derived from `userOpHash` and the actual input is `userOp`. There is no check that this `userOpHash` is the hashing of that `userOp`, and there is no check that the `userOp.sender` is the actual `msg.sender.` So anyone can do the following.
1. Make an attacking contract and install the module
2. Listen to the transactions made by the `sessionKeySigner`
3. Use the valid hash and provide different data in `userOp` (like changing the recipient of tokens)
4. Consume the signature in his favour and prevent consuming it again.
5. The real Modular wallet will not be able to execute this tx as it will revert from the `EntryPoint` execution in the validating process.
✅Recommendation:
It should be ensured that `op.sender` equals `msg.sender`. And an optional thing to do to increase the security is to reconstruct the hash from the `userOp` itself. According to the `EIP-4337` hashing method. Instead of taking he hash and dealing with it as it is the correct hashing of `userOp`.