3MIKAN
仮想通貨直コン

ERC-20権限を比較|approve・Permit・Permit2・ERC-7674

ERC-20 approve、ERC-2612 Permit、Permit2、ERC-7674を、署名、nonce、deadline、spender、state保存先、失効方法とlocal testで比較します。

3MIKANのブランドキャラクターが同じtoken vaultとspender gateにつながる4種類の権限keyを見比べる記事画像

ERC-20の権限方式を選ぶときは、誰が、どのtokenを、どのspenderへ、いくら、いつまで使わせるかを同じ順序で確認します。

approve、ERC-2612 Permit、Permit2、ERC-7674は、どれもtokenを第三者が使う権限に関係します。ただし、署名の有無だけでなく、権限stateの保存先、nonce、期限、止め方が違います。

特に「署名だからgas不要で安全」「Permit2ならtoken approvalは不要」「temporary approvalなら既存allowanceを無視できる」という理解は正しくありません。この記事では4方式を同じ比較軸へ置き、OpenZeppelin Contracts 5.6.1、Solidity 0.8.36、Foundry 1.8.0のlocal再現例で正常系と失敗系を確認します。

外部RPC、実在wallet、canonical Permit2 contract、mainnet token、実資産は使いません。

まず4方式を同じ表で分ける

方式 ownerの起点 署名 権限stateの保存先 nonce・期限 対応条件
ERC-20 approve tokenへのtransaction 不要 tokenのpersistent allowance ERC-20標準にはnonce・期限なし ERC-20 token
ERC-2612 Permit typed data署名 + 提出transaction EIP-712 tokenのpersistent allowance ownerごとの連番nonce、提出deadline token自身がERC-2612を実装
Permit2 AllowanceTransfer token→Permit2 approval + Permit2署名またはtransaction EIP-712 token allowanceとPermit2内のdownstream allowance ordered nonce、sigDeadlineexpiration token approvalとPermit2対応spender
Permit2 SignatureTransfer token→Permit2 approval + 1回の署名transfer EIP-712 downstream allowanceは保存しない。token allowanceは別に残る unordered nonce bitmap、deadline token approvalとPermit2対応spender
ERC-7674 同じtransaction内のtemporaryApprove 仕様上は不要 tokenのtransient allowance transaction境界でclear token実装、EIP-1153対応EVM、atomicな呼び出し経路

「gasless」は、ownerがapprove transactionを直接送らなくてもよい、という意味で使われます。署名をchain上で使うtransactionと、そのgasを支払う主体まで消えるわけではありません。

次の図では、権限がどのcontractへ残るかを先に分けます。

同じownerとtokenからspenderへ進むapprove、ERC-2612、Permit2、ERC-7674で、権限stateを保存するcontractが異なる流れ
Permit2だけは、tokenからPermit2へのapprovalと、Permit2から実際のspenderへの権限を二層に分けます。

ERC-20 approveはtokenへpersistent allowanceを保存する

ERC-20approve(spender, value)は、msg.senderをownerとして、token contract内のowner・spender組み合わせへ値を保存します。

token.approve(spender, 100 ether);
token.transferFrom(owner, recipient, 40 ether);

// 標準的な有限allowanceなら残りは60
token.allowance(owner, spender);

基本仕様にはdeadlineもnonceもありません。有限allowanceならtransferFromのたびに減り、approveを再実行すると新しい値へ上書きされます。使い切らず、上書きもrevokeもしなければ、別transactionへ持ち越されます。

権限を止める一般的な形はapprove(spender, 0)です。ただし、0から別の値へ変更する必要があるtokenなど、標準外の挙動もあります。productionでは対象tokenの実装と現在allowanceを先に読みます。

approveは、権限を設定するtransactionと、spenderが使うtransactionの二段階です。最初のtransactionを署名へ置き換えるのがERC-2612です。

ERC-2612 Permitは署名でtoken allowanceを更新する

ERC-2612は、次のmessageをEIP-712で署名します。

Permit(
  address owner,
  address spender,
  uint256 value,
  uint256 nonce,
  uint256 deadline
)

domainには一般にtoken名、version、chainId、verifying contractであるtoken addressを入れます。同じmessageでもchainまたはtoken contractが違えば別digestです。

署名を受け取ったrelayerやdapp contractは、tokenのpermitをtransactionで呼び出せます。条件を満たすとtokenはownerのnonceを1つ進め、通常のpersistent allowanceを更新します。

deadlineは署名提出の期限

ここは誤解しやすいところです。

ERC-2612のdeadlineは、その署名をpermitへ提出できる期限です。期限内にpermitが成功して作られたallowanceが、deadline到達時に自動で0になるわけではありません。

たとえば18時まで有効な署名を17時に提出し、100 tokenのallowanceが作られた場合、その残りは18時以降もtoken stateに残り得ます。止めるには、allowanceを使い切るか、上書きするか、0へ変更します。

nonceは署名のreplayを止める

OpenZeppelin Contracts 5.6.1ERC20Permitは、現在のnonces(owner)をdigestへ入れます。成功時にnonceを消費するため、同じ署名をもう一度出すと、現在nonceで作るdigestと一致せず失敗します。

一方、permitは誰でも提出できます。第三者が先に同じ署名を提出しても、署名が表すのはallowanceであり、その後の操作まで独占的に表すとは限りません。permit失敗だけを理由に全体を止めるか、すでに作成済みのallowanceを使って処理を続けるかは、application側で設計します。

domain、type、message、nonce、deadlineから最終digestを作る順序は、EIP-712署名のdomain・nonce・deadline検証で段階ごとに確認できます。

Permit2はtoken approvalとdownstream権限を分ける

Permit2は1つのcontractで、AllowanceTransferSignatureTransferを提供します。どちらも、先にownerがtoken contractでPermit2自身をspenderとしてapproveしていることが前提です。

layer 1: token allowance
owner ── approve ──> Permit2

layer 2: Permit2 permission
owner + token ── permit ──> downstream spender

つまり、Permit2署名が失効しても、layer 1のtoken→Permit2 allowanceまで自動で0になるとは限りません。通常のApproval CheckerでPermit2がspenderとして見えるstateと、Permit2内部の実際のdapp spender・amount・expirationは別々に確認します。

2026-08-30に確認したofficial repositoryでは、0x000000000022D473030F116dDEE9F6B43aC78BA3というdeployment-address tagがcommit cc306b601f172c51bc04334a109e98340456620bを指していました。この記事はこのcommitへsourceを固定して照合し、どのpublic chainのdeploymentにもcallしていません。

AllowanceTransferはamount・expiration・ordered nonceを保存する

AllowanceTransferはowner・token・spenderごとに、次の値をpacked stateへ保存します。

amount      uint160
expiration  uint48
nonce       uint48

署名するPermitSingleには、token、amount、expiration、nonce、spender、sigDeadlineが入ります。

  • sigDeadline: 署名をPermit2へ提出できる期限
  • expiration: 作成されたdownstream allowanceを使える期限
  • nonce: 同じowner・token・spenderで次に受け付ける連番

ERC-2612と違い、署名の提出期限と保存allowanceの期限が別fieldです。frontendで片方だけを表示すると、署名がいつまで提出できるか、作成後の権限がいつ切れるかを区別できません。

SignatureTransferはdownstream allowanceを残さない

SignatureTransferは、signed maximum amountの範囲でtokenを1回移動し、Permit2内にdownstream allowanceを残しません。

replay防止にはunordered nonce bitmapを使います。連番の次だけを待つのではなく、nonceをword位置とbit位置へ分け、使ったbitを立てます。そのため、複数の未使用署名を順不同で実行できます。

基本のPermitTransferFromが署名へ含める主な値は、token、maximum amount、spender、nonce、deadlineです。spenderはcall時のmsg.senderへ結びつきます。

一方、transfer detailsのrecipientと実際のrequested amountは、基本形の署名structへそのまま入るfieldではありません。requested amountはsigned maximum以下か検証されます。recipientや注文内容も署名へ固定したい場合は、permitWitnessTransferFromのwitness設計を含めて確認します。

「1回だけ」は、tokenからPermit2へのlayer 1 approvalが消える意味ではありません。また、signatureを見ただけでrecipient、route、最終effectがすべて拘束されているとも限りません。

ERC-7674はtransaction内だけのtemporary allowance

ERC-7674は、2026-08-30時点でReview statusです。Finalではなく、peer review中の仕様として扱います。

追加する中心のfunctionは次の1つです。

function temporaryApprove(address spender, uint256 value)
    public
    returns (bool success);

temporary allowanceは、作成した同じtransactionの間だけ有効です。複数回のtransferFromで合計valueまで使えますが、未使用分を含めてtransaction終了時にclearされます。

OpenZeppelin Contracts 5.6.1では、実装pathもdraft-ERC20TemporaryApproval.solです。EIP-1153のTSTORE / TLOADを使い、temporary allowanceをpersistent allowanceとは別のtransient slotへ保存します。

allowanceはtemporaryとpersistentの合計を返す

ERC-7674対応tokenのallowance(owner, spender)は、同じtransaction内では次の合計を返します。

visible allowance = temporary allowance + persistent allowance

transferFromはtemporary分を先に消費し、不足分だけpersistent allowanceから引くのが仕様の推奨順です。そのため、temporaryを設定しても、以前から残るpersistent allowanceが無効になるわけではありません。

OpenZeppelinのSafeERC20も、temporary allowanceが非0の状態でsafeIncreaseAllowancesafeDecreaseAllowanceを使うと、合計値をpersistent allowanceとして書き戻して予想外になる危険を説明しています。temporary対応tokenでは、表示された合計と永続stateを混同しないでください。

plainなEOAは2つのcallを別transactionへ分けられない

EOAがtemporaryApproveだけを1 transactionとして送り、次のtransactionでspenderがtransferFromしても、temporary allowanceはすでにclearされています。

grantとspendを同じtransactionへまとめるには、contract owner、smart accountのbatch、または同じowner文脈でatomicに呼ぶ統合が必要です。local Anvil再現例はtokenを持つcontract ownerがtemporaryApproveし、そのcallの途中でpullerにtransferFromさせます。次のRPC readではallowanceが0へ戻ることも確認します。

ここまでの期限とstateを並べると、次の違いになります。

approve、ERC-2612 Permit、Permit2 AllowanceTransfer、Permit2 SignatureTransfer、ERC-7674の有効期間と失効条件
署名を提出できる期限と、提出後に作られたallowanceの期限は別の値です。

同じ再現例で正常系と失敗系を比較する

local再現例は次の環境へ固定しています。

項目 version・設定
OpenZeppelin Contracts 5.6.1 exact pin
Solidity 0.8.36
Foundry 1.8.0
EVM Prague
chain / RPC FoundryとAnvilのlocal chainだけ
token local mintable PermissionToken
key / account deterministic local test keyとdeployしたcontractだけ
asset local test tokenのみ。市場価値なし

PermissionTokenはOpenZeppelinのERC20Permitdraft-ERC20TemporaryApprovalを継承します。そのためapprove、ERC-2612、ERC-7674はlibraryの実装を直接testします。

Permit2は、公式sourceからこの記事で検証する境界だけを切り出したLocalPermit2です。production Permit2のcopyではなく、batch、witness、ERC-1271、lockdown API、nonce invalidation API、gas最適化、特殊token互換処理を省いています。

// layer 1: token contractのpersistent allowance
token.approve(address(permit2), 100 ether);

// layer 2: owner・token・spender・amount・expiration・nonce
permit2.permit(owner, permission, signature);

// downstream spenderがPermit2を通してpullする
permit2.transferFrom(owner, recipient, 25 ether, address(token));

local modelの結果を、canonical deploymentの監査結果や互換性保証として使わないでください。統合時はofficial interface、deployment、chain、SDK version、対象tokenの挙動を改めて確認します。

test matrix

mechanism case local input expected result
approve success 100をapproveし40をpull recipient 40、残り60
approve insufficient 10に対して11をpull ERC20InsufficientAllowance
ERC-2612 success nonce 0、期限内、正しいtoken domain allowance作成、nonce 1
ERC-2612 expired deadline + 1秒 ERC2612ExpiredSignature
ERC-2612 replayed 同じ署名を2回提出 ERC2612InvalidSigner
ERC-2612 wrong domain 別token domainで署名 ERC2612InvalidSigner
Permit2 AllowanceTransfer success token approval + downstream permit 両方のallowanceが減る
Permit2 AllowanceTransfer expired sigDeadline超過 Permit2SignatureExpired
Permit2 AllowanceTransfer replayed ordered nonce 0を再提出 Permit2InvalidNonce
Permit2 AllowanceTransfer wrong domain 別verifying contractで署名 Permit2InvalidSigner
Permit2 AllowanceTransfer insufficient downstream 20に対して21 Permit2InsufficientAllowance
Permit2 SignatureTransfer success unordered nonce 513、max 40、request 35 bitを使用済みにして35を移動
Permit2 SignatureTransfer replayed nonce 513を再利用 Permit2InvalidNonce
ERC-7674 success temporary 40、同じtransactionで40 pull 成功し、境界後0
ERC-7674 combined persistent 30 + temporary 20で35 pull temporary 20を先に消費、persistent 15
ERC-7674 insufficient temporary 10に対して11 ERC20InsufficientAllowance

repository rootで次を実行すると、4つのFoundry再現例と2つのAnvil integrationをまとめて確認できます。

npm ci
CI_REQUIRE_FOUNDRY=1 npm run test:contracts

Anvil integrationは別transactionからallowanceを読み、unused temporary allowanceも0へ戻ることを確認します。mainnet / testnet RPC環境変数は使いません。

revoke・expiration・one-time useを分ける

方式 署名・grantを止める 作成済み権限を止める 別に残り得るもの
approve 署名前なら送らない overwrite、approve(0)、spend token allowance
ERC-2612 deadline、nonce変化 token allowanceをoverwrite / 0 deadline後も作成済みallowance
Permit2 AllowanceTransfer sigDeadline、ordered nonce更新 expiration、lockdown、amount 0、spend token→Permit2 allowance
Permit2 SignatureTransfer deadline、unordered nonce invalidation 1回使用でnonce bitを消費 token→Permit2 allowance
ERC-7674 transactionを実行しない consume、overwrite、transaction終了 既存persistent allowance

EIP-2612のnonceを進めても、すでに作成されたtoken allowanceは消えません。Permit2のdownstream permissionを止めても、tokenからPermit2へのapprovalは別stateです。

反対に、ERC-7674のtemporary stateが0へ戻っても、同じowner・spenderにpersistent allowanceがあればtransferFromはそちらを使える場合があります。「画面から一時承認が消えた」だけで最終権限を判断しません。

利用者向けにowner、token、spender、allowanceを読み、disconnectとrevokeを分ける手順は、MetaMaskのSpending cap・approve・revoke・Permitで確認できます。calldataのtoと引数のspenderを分ける場合は、ABI・function selector・event logsの読み方へ進んでください。

frontendで署名前に表示する項目

方式名だけでなく、次の実データを人が確認できる形へ直します。

  • signing account / owner
  • chainId
  • token symbolではなくtoken contract address
  • verifying contract
  • downstream spender
  • recipientまたはwitnessで拘束される注文内容
  • raw amountとdecimals適用後の数量
  • token→Permit2 allowanceの有無と上限
  • downstream amount
  • nonceの方式。orderedかunordered bitmapか
  • signature deadline
  • stored allowance expiration
  • 実行後にどのstateが残るか
  • revoke、lockdown、nonce invalidation、transaction boundaryのどれで止まるか

deadlineという同じ単語でも、署名提出期限なのか、保存権限の期限なのかをlabelで分けます。Permit2ではsigDeadlineexpirationを別行にし、layer 1とlayer 2のspender addressも分けます。

採用判断matrix

条件 向いている候補 reviewで止める点
tokenがERC-2612を正しく実装済み ERC-2612 Permit domain、proxy upgrade、nonce、permit frontrun、作成後allowance
複数tokenを共通署名interfaceで扱いたい Permit2 AllowanceTransfer token→Permit2 approval、downstream expiration、official deployment、spender code
downstream allowanceを残さず1回pullしたい Permit2 SignatureTransfer token approvalは残る、recipient / witness、unordered nonce、deadline
contract同士で同じtransactionだけ貸したい ERC-7674 Review status、token実装、EIP-1153、persistent allowanceとの合計、atomic call path
最小の標準互換性を優先する ERC-20 approve 追加transaction、長期allowance、更新race、revoke UX

どの方式も、名前だけで常に安全になるわけではありません。許可量を小さくしてもspender codeが悪意ある場合は範囲内を使えます。期限を短くしても、期限内の不正使用を止める保証にはなりません。

code reviewチェックリスト

  • owner、chain、token、spender、amountを同じ再現例で固定した
  • signatureのverifying contractと実際のcall先を照合した
  • ERC-2612対応tokenか、同名の独自permitか確認した
  • permit deadlineと作成後allowanceの寿命を分けた
  • Permit2のtoken approval層とdownstream permission層を分けた
  • AllowanceTransferのsigDeadlineexpirationを両方確認した
  • SignatureTransferのspender、recipient、witness、requested amountを確認した
  • ordered nonceとunordered nonce bitmapを取り違えていない
  • ERC-7674の現在statusとlibraryのdraft pathを記録した
  • temporary allowanceとpersistent allowanceの合計・消費順をtestした
  • grantとspendが本当に同じtransaction・owner文脈か確認した
  • expired、replayed、wrong-domain、insufficient allowanceを失敗させた
  • local modelの成功をproductionの監査・安全性保証にしていない

ERC-20権限を比較するときに大切なのは、「transactionか署名か」だけではありません。権限を保存するcontract、nonceを消費する場所、提出期限と利用期限、最後に残るallowanceまで、一つのowner・token・spender関係として説明することです。

この記事にaffiliate、token swap、wallet接続、unlimited approvalを促すCTAはありません。将来developer tooling、audit、sponsor表示を追加する場合も広告であることを明示し、仕様status、test結果、採用判断から分離します。production実装には、対象token・deployment・spenderごとの独立したsecurity reviewが必要です。

確認した一次情報