Yulでパックされたストレージ変数を読み書きする方法
執筆者 Mark Jonathas
Yulは、スマートコントラクト内でアセンブリ言語の一種を書くために使用できる中間プログラミング言語です。Yulの構文とストレージの仕組みについて学んだ後は、Yulでパックされたストレージ変数を読み書きする方法を理解することが重要です。
Yulでパックされたストレージ変数を読み書きする方法
var5を4に変更したいとします。var5はslot 3にあることが分かっているので、次のようなコードを試すかもしれません。
getValInHex(3)を使うと、slot 3が次のように書き換えられていることが分かります。
0x0000000000000000000000000000000000000000000000000000000000000004
これは問題です。var4が0に書き換えられてしまっているからです。このセクションでは、パックされた変数の読み書き方法について説明しますが、その前にYulの構文についてもう少し学ぶ必要があります。
これらの演算に馴染みがなくても心配ありません。これから例を交えて説明していきます。
まずand()から始めましょう。2つのbytes32を用意し、and()演算子を試して、何が返されるか見てみます。
出力を見ると次のようになっています。
0x0000000000000000000000009acc1d6aa9b846083e8a497a661853aae07f0f00
これは、and()が両方の入力の各ビットを見て、その値を比較するためです。
両方のビットが1である場合(バイナリで考えると、有効か無効か)、そのビットはそのまま保持されます。
それ以外の場合は0に設定されます。
次にor()のコードを見てみましょう。
今回の出力は次の通りです。
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
これは、いずれかのビットが有効かどうかを見ているためです。
mask変数を次のように変更した場合を見てみましょう。
0x00ffffffffffffffffffffff0000000000000000000000000000000000000000
ご覧の通り、出力は次のように変わります。
0x00ffffffffffffffffffffff9acc1d6aa9b846083e8a497a661853aae07f0f00
最初のバイトが0x00になっている点に注目してください。どちらの入力も最初のバイトに有効なビットを持っていないためです。
xor()は少し異なります。片方のビットが有効(1)で、もう片方のビットが無効(0)である必要があります。
コードによるデモンストレーションは以下の通りです:
出力は次の通りです。
0xffffffffffffffffffffffff0000000000000000000000000000000000000000
主な違いは、0x00と0xffが揃っている箇所でのみ出力に有効なビットが現れるという点に表れています。
shl()とshr()は互いに非常によく似た動作をします。どちらも入力値を、入力されたビット数だけシフトします。shl()は左にシフトし、shr()は右にシフトします。
コードを見てみましょう!
出力:
ans1: 0x0000ffff00000000000000000000000000000000000000000000000000000000
ans2: 0x00000000000000000000000000000000000000000000000000000000ffff0000
まずans1を見てみましょう。16ビット(2バイト)分shr()を行っています。ご覧の通り、最後の2バイトが0xffffから0x0000に変わり、最初の2バイトが2バイト分右にシフトしています。これが分かれば、ans2は自明でしょう。起こっているのは、ビットが右ではなく左にシフトされるという違いだけです。
var5に書き込む前に、まずvar4とvar5を読み取る関数を書いてみましょう。
出力は期待通り1と2です。
var4を取得するには、値を次のように設定するマスクを使うだけです。
0x0000000000000000000000000000000000000000000000000000000000000001
そして、1に等しいuint128を返します。
var5を読み取る際は、var4を右にシフトすることで取り除く必要があります。
これにより、次のような値になり、これを返すことができます。
0x0000000000000000000000000000000000000000000000000000000000000002
複数の変数(2つ以上)が1つのストレージスロットにパックされている場合、値を読み取るためにシフトとマスクを同時に行う必要がある場合があることに注意してください。
さて、いよいよvar5の値を4に変更する準備が整いました!
最初のステップは、ストレージのslot 3をロードすることです。
次に、マスクを作成する必要があります。
var4を読み取ったときと同様に、値を次のように分離したいと思います。
0x0000000000000000000000000000000000000000000000000000000000000001
次のステップは、新しい値をvar5のスロット位置に合わせてフォーマットすることです。次のようになります。
0x0000000000000000000000000000000400000000000000000000000000000000
var5を読み取ったときとは異なり、今回は値を左にシフトします。
最後に、or()を使って値を結合し、32バイトの16進数にして、その値をslot 3に格納します。
getValInHex(3)を呼び出すことで、作業内容を確認できます。
これにより、期待通り次の値が返されます。
0x0000000000000000000000000000000400000000000000000000000000000001
これで、パックされたストレージスロットの読み書き方法が分かりました!次は、YulにおけるMemory、Storage、Smart Contract Callsの仕組みについて学びましょう。
関連する概要

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


