スマートコントラクトのMethod IDを求める(@python)ことでスマコン実行の流れについて理解が深まった話

pythonからブロックチェーン上のスマートコントラクトにアクセスすることにいくつかチャレンジしてきましたが、その際によく見たMethod ID。

何気なくpythonで書きたいコントラクト内の関数特定のヒントにしていましたが、何者なのか?また、どのように求められるのかをまとめておこうと思います。


Method ID(関数セレクター)とは

ブロックチェーン上のスマートコントラクト内の関数を実行するには、RPCエンドポイントに対して、
・対話したいスマートコントラクトのアドレス
・data(関数、引数)
を投げる必要があります。
このdata部の先頭4バイトがMethod IDです。
※確かに、スマコンのアドレス、関数、引数が決まればやりたい処理が一意に決まりますね。

例えば、記事冒頭で示した画像のMethod IDについて。Input DataのView Input AsでOriginalを選択すると、data部をそのまま見られます。この先頭4バイト(0x以降の8文字)はbff25ca2がMethod IDです。また、これに続くのが引数をABI形式でエンコードした部分です。

Method IDを求める

では、Method IDはどのように求めるのか。
関数のシグネチャ(関数名と引数型の組み合わせ)、例えば"transfer(address, uint256)"といった形、をKeccak-256ハッシュ関数でハッシュ化し、その最初の4バイトがMethod IDとして使用されます。

下の記事で用いたUniswapV2Routerのswap関数について、実際に計算してみます。

Base Mainnetにおいて0.01ETH→AEROにswapするコードですが、これを実行すると以下のトランザクションが得られました。

https://basescan.org/tx/0xae345975dae892c3f8572a9ceb5a359aa07e7677a5d198909dc9c5dc0d8d5330

Method IDを確認すると、

このMethod IDを求めてみます。

UniswapV2RouterのWrite Contractからこの関数を確認すると、以下ですね。
https://basescan.org/address/0x4752ba5dbc23f44d87826276bf6fd6b1c372ad24#writeContract

ということは、
swapExactETHForTokens(uint256,address[],address,uint256)
をKeccak-256ハッシュ関数でハッシュ化すれば得られることになります。

pythonでこのコードを書いてみます。

#create_methodID.py

from web3 import Web3

# Web3インスタンスの作成
w3 = Web3()

# 関数シグネチャ
function_signature = "swapExactETHForTokens(uint256,address[],address,uint256)"

# Keccak-256でハッシュ化
method_id = w3.keccak(text=function_signature)[:4]  # 最初の4バイトを取得

# ハッシュ値を16進数で表示
print("Method ID (Keccak-256 hash):", method_id.hex())

実行結果は以下です。

確かに所望のMethod IDが得られました。

疑問(pythonでスマコン関数を実行するときにMethod IDを求めるコードなんて書いていない)

ここで疑問に思ったのは、これまでいくつかpythonでスマートコントラクトを実行してきましたが、method IDを求めるコードなんて一度も書いたことがありません。

ではなぜmethod IDをRPCエンドポイントに渡すことができているのか?

build_transactionがその役割を担っています。

今回の題材で言えば、下記の部分です。

with open('UniSwap_Router_ABI.json') as f:
   uniRouterABI = json.load(f)
uniRouterContract = web3.eth.contract(address=UniV2Router, abi=uniRouterABI)

now = int(datetime.now().timestamp()) 
nonce = web3.eth.get_transaction_count(walletAddress) 

funcSwap = uniRouterContract.functions.swapExactETHForTokens(
       0, 
       [WETH, AERO], 
       walletAddress, 
       now+200 
       )

tx = funcSwap.build_transaction({
   'value': web3.to_wei(0.01, 'ether'),
   'gas': gas,
   'gasPrice': web3.to_wei(gasPrice, 'gwei'),
   'nonce': nonce
   })

スマートコントラクトの関数を実行するには、
・対話したいコントラクトのアドレス(toフィールド)
・関数(dataフィールド)
・引数(dataフィールド)
をRCPエンドポイントに渡さなければなりません。

web3.eth.contract()でコントラクトインスタンスを作成し、その流れでbuild_tansactionでトランザクションを作成すると、自動的に「対話したいコントラクトのアドレス(toフィールド)」はインスタンス化したコントラクトのアドレスになります。

そして、build_transactionで、自動的に関数シグネチャがKeccak-256ハッシュ関数でハッシュ化されMethod IDが導かれます。また、引数をABI形式(スマートコントラクトが理解できる形式)で引数を変換し、これをMethod IDに連結します。このMethod IDとABI形式でエンコードされた引数を連結したものがdata部になります。
build_transactionはここまでのことを内部で実行してくれているんですね。

ここまで理解したうえで、もう一度実行したトランザクションを見てみます。
https://basescan.org/tx/0xae345975dae892c3f8572a9ceb5a359aa07e7677a5d198909dc9c5dc0d8d5330

toフィールドを確認すると、自動的にUniswapV2Routerのコントラクトアドレスが指定されています。

また、dataフィールドの先頭にはMethod IDがあり、それに続く部分がABI形式でエンコードされた引数、と解釈できます。

まとめ

Method IDとは、というお題を確認することで、スマートコントラクトが実行される流れの全体像を確認できた。
今回はdata部のMethod IDに続く部分については細かく確認しなかったが、ここがABIエンコードされているということを実際に確認するという作業も基礎的な理解を深めるうえで取り組んでみても良いかもしれないと思いました。


この記事が気に入ったらサポートをしてみませんか?