# Solidity 0.8.28 Release — New Features and Breaking Changes
The Solidity team released version 0.8.28 in February 2026, bringing powerful new features and some breaking changes. If you're still on 0.8.26 or earlier, here's what you need to know before upgrading.
New Features
1. Transient Storage (TSTORE/TLOAD)
The biggest addition is native support for transient storage via the transient keyword. This maps to the TSTORE and TLOAD opcodes introduced in EIP-1153 (Cancun upgrade).
What is transient storage?
Transient storage exists only for the duration of a transaction. It's cheaper than SSTORE because it doesn't persist to disk.
Gas savings:
TSTORE: 100 gas (vs 20,000 for coldSSTORE)
TLOAD: 100 gas (vs 2,100 for coldSLOAD)
Use case: Reentrancy guards
// OLD: Costs ~20k gas to write, ~2.1k to read
contract OldReentrancyGuard {
bool private _locked;
modifier nonReentrant() {
require(!_locked, "Reentrant call");
_locked = true;
_;
_locked = false;
}
}
// NEW: Costs only 100 gas per read/write
contract NewReentrancyGuard {
bool private transient _locked;
modifier nonReentrant() {
require(!_locked, "Reentrant call");
_locked = true;
_;
_locked = false;
}
}
Savings: ~39,900 gas per protected function call.
Other use cases:
- Flash loan state tracking
- Temporary allowances
- Atomic swap coordination
- Cross-contract call context
Limitations:
- Cannot be accessed after transaction ends
- Not available in
view/purefunctions
- No inheritance between contracts (each has isolated transient storage)
2. Improved Error Messages
Compiler error messages are now significantly clearer, especially for type mismatches and inheritance issues.
Before (0.8.26):
TypeError: Member "value" not found or not visible after argument-dependent lookup in address.
After (0.8.28):
TypeError: Member "value" not found in address.
Did you mean to use "address.balance"?
Suggestions: balance, call, delegatecall
This alone will save hours of debugging for beginners learning Solidity.
3. Custom Errors in Require Statements
You can now use custom errors with require:
error InsufficientBalance(uint256 available, uint256 required);
function withdraw(uint256 amount) external {
// OLD syntax (still works)
require(balance >= amount, "Insufficient balance");
// NEW syntax with custom error
require(balance >= amount, InsufficientBalance(balance, amount));
balance -= amount;
}
This gives you the gas savings of custom errors with the simplicity of require syntax.
4. Inline Assembly Improvements
New inline assembly features:
verbatimblocks for injecting raw bytecode
- Improved type checking for Yul variables
- Better error messages for invalid opcodes
function getChainId() external view returns (uint256 id) {
assembly {
id := chainid()
}
}
Breaking Changes
1. Stricter Type Conversion Rules
Implicit conversions between address and address payable are now more restricted.
// This no longer compiles
address payable recipient = msg.sender; // ERROR
// You must explicitly cast
address payable recipient = payable(msg.sender); // OK
2. Function Visibility in Interfaces
Functions in interfaces must now be explicitly marked external. The public visibility is no longer allowed.
// OLD (0.8.26) — worked but deprecated
interface IERC20 {
function transfer(address to, uint256 amount) public returns (bool);
}
// NEW (0.8.28) — required
interface IERC20 {
function transfer(address to, uint256 amount) external returns (bool);
}
3. Deprecated now Keyword Removed
The now keyword (alias for block.timestamp) is fully removed. Use block.timestamp instead.
// This no longer compiles
uint256 deadline = now + 1 days; // ERROR
// Use this instead
uint256 deadline = block.timestamp + 1 days; // OK
Migration Guide
Step 1: Update Compiler Version
In hardhat.config.ts or foundry.toml:
// Hardhat
solidity: {
version: "0.8.28",
settings: {
optimizer: { enabled: true, runs: 200 }
}
}
# Foundry
[profile.default]
solc_version = "0.8.28"
Step 2: Fix Breaking Changes
Run the compiler and fix errors one by one:
npx hardhat compile
# or
forge build
Common fixes:
- Replace
nowwithblock.timestamp
- Add
payable(...)casts for address conversions
- Change
publictoexternalin interfaces
Step 3: Adopt New Features
Look for optimization opportunities:
- Replace storage-based reentrancy guards with transient storage
- Use custom errors in
requirefor better error handling
- Refactor temporary state variables to
transient
Step 4: Test Thoroughly
Run your full test suite:
npx hardhat test
# or
forge test -vvv
Pay special attention to:
- Reentrancy guards behavior
- Gas usage (transient storage should reduce costs)
- Error message clarity in tests
Should You Upgrade?
Upgrade if:
- You want 99% gas savings on reentrancy guards
- You have temporary state variables (prime candidate for
transient)
- You're starting a new project
Wait if:
- You're about to deploy to mainnet (test 0.8.28 on testnet first)
- You have complex assembly code (test thoroughly)
- Your codebase is very large (migration effort may not be worth it immediately)
Resources
- Transient Storage Tutorial — Learn to use transient storage effectively
Conclusion
Solidity 0.8.28 is a solid upgrade focused on developer experience and gas optimization. Transient storage alone makes it worth upgrading for most projects. The breaking changes are minimal and easy to fix.
Start experimenting with transient storage today — it's a powerful primitive that will shape how we write smart contracts going forward.