📚

ERC20Permitについて

2024/09/30に公開

ERC20Permitとは

EIP-2612で提案されたものです。
https://eips.ethereum.org/EIPS/eip-2612

簡単に説明すると、
ERC20の送金権限を自分(Owner)以外のコントラクトやEOAに付与する場合はapproveが必要になります。このapproveのガス代をユーザーに負担させないための仕組みです。

何が変わるのか

例えばUniswapのPoolに流動性を提供する場合を考えます。

Permitを使用しない場合

  1. User: Uniswapのコントラクトアドレスに対して必要な数量のapproveをする(Userがガス代を負担)
  2. User: addLiquidityで流動性を提供する(Userがガス代を負担)

二度のトランザクションとガス代の負担が発生します。

Permitを使用する場合

  1. User: オフチェーンでapprove用の署名を作成(オフチェーンなのでガスは発生しない)
  2. Spender: Userの署名を使ってオンチェーンでpermitを実行(Spenderがガス代を負担)
  3. User: addLiquidityで流動性を提供する(Userがガス代を負担)

Userは本来実行したいaddLiquidityのガス代を払うだけで済みます。

コード

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/ERC20Permit.sol

パラメータ

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);

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol

これでapproveされます。

以上!

Discussion