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

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、sigDeadline、expiration |
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へ残るかを先に分けます。
ERC-20 approveはtokenへpersistent allowanceを保存する
ERC-20のapprove(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.1のERC20Permitは、現在の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で、AllowanceTransferとSignatureTransferを提供します。どちらも、先に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の状態でsafeIncreaseAllowanceやsafeDecreaseAllowanceを使うと、合計値を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を並べると、次の違いになります。
同じ再現例で正常系と失敗系を比較する
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のERC20Permitとdraft-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ではsigDeadlineとexpirationを別行にし、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の
sigDeadlineとexpirationを両方確認した - 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が必要です。
確認した一次情報
- ERC-20 Token Standard確認日: 2026/08/30
- ERC-2612 Permit Extension確認日: 2026/08/30
- Uniswap Permit2 canonical deployment-address tag確認日: 2026/08/30
- Uniswap Permit2 AllowanceTransfer pinned source確認日: 2026/08/30
- Uniswap Permit2 SignatureTransfer pinned source確認日: 2026/08/30
- ERC-7674 Temporary Approval Extension確認日: 2026/08/30
- EIP-1153 Transient storage opcodes確認日: 2026/08/30
- OpenZeppelin Contracts v5.6.1確認日: 2026/08/30
- OpenZeppelin ERC20Permit v5.6.1確認日: 2026/08/30
- OpenZeppelin draft ERC20TemporaryApproval v5.6.1確認日: 2026/08/30
- Solidity 0.8.36 documentation確認日: 2026/08/30
- Foundry v1.8.0確認日: 2026/08/30



