ERC20のtransferFrom関数完全に理解した。
概要
ERC20に内蔵されているtransferFrom関数を使用するスマコンを作成している時に詰まってしまい、小一時間ChatGPTと会話して完全に理解したので復習を兼ねてこの記事で解説していきます。完全に理解して使いこなしたい人は要チェックです。
前提としてこのtransferFrom
関数は第三者によるトークンの操作を許可する関数ですが、この関数を使用するには少しコツがいります。
とりあえずtransferFrom関数に触れる
transferFrom関数のコードチェック
function transferFrom(address from, address to, uint256 amount) external returns (bool);
上記はERC20のインターフェイスが定義されたIERC20内にあるtransferFrom
関数です。一見するとこの関数をinterfaceを通してコントラクトに置き、第三者からの残高操作を実行したいEOA(アドレス)に叩かせることで難なく通りそうです 😃❗️
function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
上記はERC20のコードです。どうやらtransferFrom
関数は_spendAllowance
という関数をラップしているようです。この_spendAllowance
は更に_approve
(EOAの残高を第三者が操作することを許可する関数)をラップしているので、やはりtransferFrom
を叩けばEOAの残高を操作できそうです❗️
暗雲が立ち込める
_allowance構造体
mapping(address => mapping(address => uint256)) private _allowances;
_allowance
とはそのアドレスがいくらの(allowance)
トークンの操作を誰(spender)
に許可したかを保存している構造体です。どうやら先ほど登場した_spendAllowance
はallowance
関数を経由してコレを参照しているようです。
_spendAllowance関数
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
今先ほどから幾度となく登場しているので、この辺りで_spendAllowance
を確認しておきましょう。
今先ほど説明した通りallowance
を叩いてるようです。このallowance
は_allowance
構造体を参照してcurrentAllowance
に格納をしているようです。そして_approve
関数で操作を許可されたトークン量(allowance)
から送金する量(amount)
をひいています。
......
ココで呼ばれてるapproveは更新作業をしているだけでは...?
ということは最初にapprove
を叩いてからtransferFrom
を実行しなければ、一銭もトークンの操作を許可されていないのでエラーが発生してしまいます。
結論
transferFrom関数を叩く前にapprove関数を実行しよう
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
よく確認すればowner
には _msg.sender が格納されるので、EOA自らがこの関数を叩かなければいけなかったようです。
この関数の実行に署名させてから、コントラクト側での処理も署名させましょう!
今回は自らの失態と引き換えにコードチェックの重要性を学べたのでヨシとします。
Discussion