Yulにおけるメモリの仕組み
執筆者 Mark Jonathas
Yulは、スマートコントラクト内でアセンブリ言語の一種を書くために使用できる中間言語です。Yulのstorageと、パックされたstorage変数の読み書き方法について学んだら、次はYulスマートコントラクトにおけるメモリの動作について学ぶことが重要です。
Yulにおけるメモリの動作
メモリはstorageとは異なる動作をします。メモリは永続的ではなく、関数の実行が終わるとすべての変数がクリアされます。
メモリは他の言語におけるヒープに似ていますが、ガベージコレクタは存在しません。
メモリはstorageよりもはるかに安価です。最初の22ワードのメモリコストは線形に計算されますが、それ以降はメモリコストが二次関数的に増加するため注意が必要です。
メモリは32バイト単位で配置されます。詳しくは後述しますが、ここでは0x00 - 0x20が1つの単位であることを理解しておいてください(スロットのようなものと考えても構いませんが、両者は異なるものです)。
Solidityは0x00 - 0x40をスクラッチスペースとして割り当てます。このメモリ領域が空であることは保証されておらず、特定の操作に使用されます。
0x40 - 0x60には、フリーメモリポインタと呼ばれるものの位置が格納されており、これは新しい値をメモリに書き込む際に使用されます。
0x60 - 0x80は空のギャップとして残されています。
0x80から実際の操作を開始します。
メモリは値をパックしません。storageから取得した値は、それぞれ独自の32バイト単位で格納されます(例:0x80-0xa0)。
Yulでメモリを使用する操作とは
メモリは以下の操作で使用されます。
- 外部呼び出しの戻り値
- 外部呼び出しの関数値の設定
- 外部呼び出しからの値の取得
- エラー文字列を伴うrevert
- ログメッセージ
- keccak256()によるハッシュ化
- 他のスマートコントラクトの作成
メモリに関する便利なYul命令を紹介します:
続いて、さらに別のデータ構造を見ていきましょう!
Yulでstructとメモリを使う方法
structと固定長配列は実際には同じように動作しますが、固定長配列についてはすでにstorageのセクションで扱ったため、ここではstructを見ていきます。以下のstructをご覧ください。
特に変わったところはなく、単純なstructです。
それでは実際のコードを見てみましょう!
ここでは、s.subVar1をメモリ位置0x80 - 0xa0に、s.subVar2をメモリ位置0xa0 - 0xc0に設定しています。そのため、0x80 - 0xc0をreturnしています。トランザクション終了直前のメモリレイアウトを表にまとめると以下のようになります。
ここから分かること:
- 0x00 - 0x40はスクラッチスペースとして空になっている
- 0x40はフリーメモリポインタを示している
- Solidityは0x60にギャップを残している
- 0x80と0xa0はstructの値の格納に使われている
- 0xc0は新しいフリーメモリポインタになっている
このメモリのセクションの最後に、動的配列がメモリ上でどのように扱われるかを見ていきます。この例では、パラメータarrとして[0, 1, 2, 3]を渡します。さらにこの例では、配列に要素を1つ追加してみます。ただし本番環境でこれを行う際は、別のメモリ変数を上書きしてしまう可能性があるため注意してください。
コードは以下の通りです:
ここで行われていることは以下の通りです:
- 配列がメモリ上のどこに格納されているかを取得する
- 配列の先頭のメモリ位置に格納されている、配列の長さを取得する
- その位置に32バイトを加算し(配列の長さ部分をスキップし)、次に使用可能な位置を確認する
- 配列の長さに32バイトを掛けて、次のメモリ位置まで進める
- 新しい値(4)を格納する
- 配列の長さを1増やして更新する
- フリーメモリポインタを更新する
- 配列をreturnする
もう一度メモリレイアウトを見てみましょう。
以上でメモリに関するセクションは終わりです!
次は、Yulを使ってスマートコントラクトを呼び出す方法を学ぶか、パックされたstorage変数の読み書き方法に戻って学び直してください!
関連する概要

ブロックチェーンで魔法を生み出す
Alchemyは、最も強力なweb3開発者向けプロダクトとツールを、豊富なリソース、コミュニティ、そして卓越したサポートと組み合わせて提供します。


