📚
ERC20Permitについて
ERC20Permitとは
EIP-2612で提案されたものです。
簡単に説明すると、
ERC20の送金権限を自分(Owner)以外のコントラクトやEOAに付与する場合はapprove
が必要になります。このapprove
のガス代をユーザーに負担させないための仕組みです。
何が変わるのか
例えばUniswapのPoolに流動性を提供する場合を考えます。
Permitを使用しない場合
- User: Uniswapのコントラクトアドレスに対して必要な数量のapproveをする(Userがガス代を負担)
- User: addLiquidityで流動性を提供する(Userがガス代を負担)
二度のトランザクションとガス代の負担が発生します。
Permitを使用する場合
- User: オフチェーンでapprove用の署名を作成(オフチェーンなのでガスは発生しない)
- Spender: Userの署名を使ってオンチェーンでpermitを実行(Spenderがガス代を負担)
- User: addLiquidityで流動性を提供する(Userがガス代を負担)
Userは本来実行したいaddLiquidityのガス代を払うだけで済みます。
コード
パラメータ
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
...
}
- owner: トークン所有者のアドレス。
- spender: トークンを引き出す許可を得るアドレス。
- value: 引き出すトークンの量。
- deadline: 署名が有効な期限。これを過ぎると署名は無効になります。
- v, r, s: ECDSA 署名のパラメータ。
署名の有効期限チェック
Userが署名する際に有効期限を決めます。
その有効期限を過ぎていないかのチェックです。
if (block.timestamp > deadline) {
revert ERC2612ExpiredSignature(deadline);
}
署名の確認
ECDSA.recoverで署名者のアドレスを復元しトークンのownerと一致しているか確認します。
bytes32 structHash = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));
bytes32 hash = _hashTypedDataV4(structHash);
address signer = ECDSA.recover(hash, v, r, s);
if (signer != owner) {
revert ERC2612InvalidSigner(signer, owner);
}
approveの実行
ERC20のapproveを実行します。
_approve(owner, spender, value);
これでapproveされます。
以上!
Discussion