ストレージ最適化
パッキングとスロット
「ストレージ最適化」はCoddyKit上の無料Web3 & DApp Development Fundamentalsレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはWeb3 & DApp Development Fundamentals学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Web3 & DApp Development Fundamentalsコースには全4レッスンが含まれています。
このレッスンの一部はまだ翻訳されておらず、英語で表示されています。
Storage Slots Basics
Contract storage is a giant array of 32-byte slots, indexed from slot 0. State variables are assigned to slots in declaration order.
Every slot you write costs gas, so the fewer slots your data occupies, the cheaper your contract is to use. Understanding the slot layout is the foundation of storage optimization.
contract Layout {
uint256 a; // slot 0
uint256 b; // slot 1
uint256 c; // slot 2
}Variable Packing
The Solidity compiler packs multiple small variables into a single 32-byte slot when they fit consecutively.
A uint128 uses 16 bytes, so two of them share one slot. A bool uses 1 byte and a uint8 uses 1 byte. Packing reduces both deployment and runtime storage costs.
contract Packed {
uint128 a; // slot 0 (bytes 0-15)
uint128 b; // slot 0 (bytes 16-31)
uint256 c; // slot 1
}Declaration Order Matters
Packing only works for variables that are adjacent in declaration. A poorly ordered layout wastes slots.
In the bad example below, the uint256 forces the two uint128s into separate slots. Reordering so the small types are together lets them pack.
// BAD: 3 slots
uint128 a; // slot 0
uint256 b; // slot 1
uint128 c; // slot 2
// GOOD: 2 slots
uint128 a; // slot 0
uint128 c; // slot 0
uint256 b; // slot 1Choosing Integer Sizes
Smaller integers only save gas when they pack. A lone uint8 in its own slot is not cheaper than a uint256, and may even cost more because of masking operations.
Rule of thumb: use uint256 by default, and only downsize when you can fit several small values into one slot.
Packing Structs
Structs follow the same packing rules. Order struct members so small types group together. A well-packed struct can cut the number of SSTORE operations dramatically.
struct Order {
uint128 amount; // slot 0
uint64 timestamp;// slot 0
uint64 id; // slot 0
address maker; // slot 1
}Constants and Immutables
Values that never change should not live in storage at all.
constant— inlined into bytecode at compile time, zero storageimmutable— set once in the constructor, stored in bytecode not storage
Both avoid expensive SLOAD operations entirely.
uint256 public constant MAX_SUPPLY = 10000;
address public immutable owner;
constructor() {
owner = msg.sender;
}Mappings and Dynamic Arrays
Mappings and dynamic arrays do not pack. Each entry occupies its own slot computed by hashing the key with the base slot.
You cannot pack across mapping entries, but you can pack the struct stored as a mapping value. Designing the value struct to fit in fewer slots saves gas on every write.
mapping(address => Order) public orders;
// each Order packs into 2 slots regardless of mappingCaching Storage Reads
Reading a storage variable repeatedly inside a function pays for each access. Copy it into a local memory variable once, then use the local.
This is especially valuable inside loops, where each iteration would otherwise re-read storage.
uint256 len = items.length; // cache length once
for (uint256 i = 0; i < len; i++) {
// process items[i]
}Batched Writes
When updating multiple fields of a packed struct, load the struct into memory, modify it, then write it back once. The compiler can combine adjacent fields into a single SSTORE.
Writing fields one at a time may trigger multiple read-modify-write cycles on the same slot.
Order memory o = orders[id];
o.amount = newAmount;
o.timestamp = uint64(block.timestamp);
orders[id] = o; // single packed write of slot 0Zero to Non-Zero Penalty
Initializing a slot from zero to non-zero costs 20000 gas, while updating an already non-zero slot costs only 5000 gas.
This is why some contracts pre-warm slots or use a sentinel value (like 1 instead of 0) to keep slots non-zero, trading a one-time cost for cheaper repeated updates.
// Using 1 as 'false' and 2 as 'true' keeps the slot non-zero
uint256 private locked = 1; // never goes back to 0Layout Audit Checklist
When auditing storage for gas:
- Group small types so they pack into shared slots
- Use
constant/immutablefor fixed values - Cache repeated storage reads in memory
- Batch struct writes
- Avoid unnecessary zero to non-zero transitions
Always verify with a gas report after changes.
Quick Check
You declare a uint128, then a uint256, then another uint128. How many storage slots do they use?
Recap
You learned to optimize storage layout:
- Storage is 32-byte slots; adjacent small types pack together
- Declaration order determines packing
constantandimmutableavoid storage entirely- Cache reads and batch struct writes
- Mind the zero to non-zero write penalty
Next we look at optimizing loops and calldata handling.
よくある質問
「ストレージ最適化」レッスンは無料ですか?
はい。「ストレージ最適化」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Web3 & DApp Development Fundamentalsコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Web3 & DApp Development Fundamentalsコースには全4レッスンが含まれています。
「ストレージ最適化」で何を学びますか?
パッキングとスロット ブラウザで直接実行するハンズオンコードでWeb3 & DApp Development Fundamentalsを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Web3 & DApp Development Fundamentalsを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのWeb3 & DApp Development Fundamentalsは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。
「ストレージ最適化」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このWeb3 & DApp Development Fundamentalsレッスンでコードを書いて実行できますか?
はい。すべてのWeb3 & DApp Development Fundamentalsレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。