Solidity 0.8.28: New Features and Breaking Changes
A complete breakdown of everything new in Solidity 0.8.28, including transient storage, custom errors improvements, and what may break in your existing code.
What Changed in Solidity 0.8.28
Solidity 0.8.28 landed in late 2025 with several quality-of-life improvements and a few breaking changes that affect production codebases. Understanding the delta between 0.8.27 and 0.8.28 is essential before upgrading any deployed project.
The headline features include improved transient storage integration via the TSTORE/TLOAD opcodes introduced in EIP-1153, better custom error ABI encoding, and tighter restrictions on implicit type conversions that were previously silently accepted.
Breaking changes to watch for: the compiler now rejects certain implicit conversions between integer types that were previously warnings. Contracts relying on silent uint256 to uint128 truncation will fail to compile. The change makes intent explicit and prevents a class of subtle bugs.
Transient Storage: TSTORE and TLOAD
EIP-1153 transient storage was activated in the Cancun/Dencun upgrade, and Solidity 0.8.28 deepens first-class language support for it. Transient storage variables persist only for the duration of a transaction — they reset to zero after each transaction completes, similar to memory, but they are accessible across internal calls like storage.
Declaring a transient variable uses the new transient keyword:
uint256 transient private _reentrancyGuard;
This single line replaces the classic storage-based reentrancy guard pattern, cutting the gas cost of a reentrancy lock from roughly 20,000 gas (cold SSTORE) to around 100 gas (TSTORE).
Practical uses: reentrancy guards, flash loan state, inter-contract call context passing within a single transaction, and temporary approval caches.
Audit note: transient variables do NOT persist across transactions. Any code that assumes state will survive into the next block will silently break. Document transient variables carefully.
Custom Errors and ABI Encoding Improvements
Custom errors introduced in Solidity 0.8.4 have been progressively improved. In 0.8.28, the ABI encoding of custom errors is now consistent with event encoding, which was a long-standing inconsistency that caused issues in off-chain decoders.
Previously, some error parameters with complex types were encoded differently depending on whether they came from a revert or an event. This inconsistency has been resolved — the ABI encoding is now canonical for both.
If you have off-chain tooling (indexers, SDKs, frontend error parsers) that decode custom errors, verify they handle the updated encoding. Libraries like viem and ethers.js v6 already account for this.
Additionally, 0.8.28 allows custom errors to be defined at the file level outside of contracts, making them reusable across multiple contracts in the same project without inheritance.
Stricter Type Conversion Rules
One of the most practically impactful changes in 0.8.28 is the tightening of implicit type conversion rules. The compiler now emits an error (not just a warning) for conversions that could silently truncate values.
Common patterns that now require explicit casting:
Assigning a uint256 to a uint128 without explicit casting now fails compilation. You must write uint128(value) to make truncation intent clear.
Passing a larger integer type to a function expecting a smaller type now requires explicit conversion.
Bytes truncation: assigning bytes32 to bytes16 without explicit slicing now errors.
Migration strategy: run the new compiler against your codebase first and collect all type errors. For each one, decide whether the truncation is intentional. If yes, add the explicit cast. If no, you have found a real bug.
The strictness is welcome — implicit truncation has been the root cause of several real exploits where a value silently wrapped.
Upgrading Your Project to 0.8.28
A structured upgrade path minimizes risk:
Step 1 — Update the pragma. Change pragma solidity ^0.8.x to pragma solidity ^0.8.28 in a separate branch.
Step 2 — Run the compiler and collect all errors. Do not ignore warnings — treat them as errors with --error-reporter.
Step 3 — Fix type conversion issues explicitly. Do not blindly cast — verify the intent of each conversion.
Step 4 — Migrate reentrancy guards to transient storage where appropriate. This is optional but recommended for gas savings.
Step 5 — Re-run your full test suite. Foundry and Hardhat both support 0.8.28. Ensure 100% of existing tests pass before merging.
Step 6 — Re-run static analysis tools (Slither, Mythril) against the updated codebase. New compiler versions sometimes surface new findings.
Solidity 0.8.28 is a net positive upgrade — the breaking changes are minor and the gas savings from transient storage alone justify the migration effort for most production contracts.