From bb51bf6e7acddc87c458407026447a59d1eaf727 Mon Sep 17 00:00:00 2001 From: howy <132113803+howydev@users.noreply.github.com> Date: Tue, 10 Feb 2026 11:37:18 -0500 Subject: [PATCH] feat(tip-1021): add permit extensions for TIP-20 Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-019c7266-9213-726e-8660-aca915eaff69 --- tips/tip-1021.md | 115 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 tips/tip-1021.md diff --git a/tips/tip-1021.md b/tips/tip-1021.md new file mode 100644 index 0000000000..8f963264a2 --- /dev/null +++ b/tips/tip-1021.md @@ -0,0 +1,115 @@ +--- +id: TIP-1021 +title: TIP-20 Permit Extensions +description: Extends TIP-1004 permit with EIP-1271 signature validation and a bytes-signature overload, enabling permit for accounts using non-secp256k1 keys such as P256 and WebAuthn, as well as smart contract wallets. +authors: Howy +status: Draft +related: TIP-1004, TIP-1020 +--- + +# TIP-1021: TIP-20 Permit Extensions + +## Abstract + +TIP-1021 adds a `permit()` overload to TIP-20 tokens to enable permit functionality for smart contract accounts and Tempo EOAs created with P256 or Webauthn keys. + +## Motivation + +TIP-1004 introduces EIP-2612 permit to TIP-20 tokens. However, the `permit(owner, spender, value, deadline, v, r, s)` signature relies on `ecrecover`, which only works with secp256k1 ECDSA keys. This excludes a growing category of accounts on Tempo: + +- **Tempo accounts with P256 or WebAuthn keys**: In the Tempo account model, EOAs can be created with P256 or WebAuthn keys instead of secp256k1. These accounts have valid signing keys, but their signatures are not recoverable via `ecrecover`, making them incompatible with the existing permit flow. +- **Smart contract wallets**: Smart contract wallets (e.g., Safe, Argent) and account-abstraction wallets are seeing increasing adoption across the ecosystem. These accounts cannot produce ECDSA signatures from their contract address, so they are also unable to use EIP-2612 permit. + +As permit becomes a critical primitive for gasless approvals, single-transaction DeFi flows, and token sweeps, excluding these account types creates a fragmented experience where only accounts with secp256k1 keys can benefit. + +This TIP extends the TIP-1004 permit with [EIP-1271](https://eips.ethereum.org/EIPS/eip-1271) signature validation and adds a `permit(owner, spender, value, deadline, bytes signature)` overload that accepts arbitrary-length signatures, enabling permit for all account types on Tempo. + +### Alternatives + +Tempo transactions solves for many usages for token transfers but not all usages. Examples include: +1. Sweeping tokens from addresses that have not previously made an approval +2. Gasless CoW protocol-style solvers that batch-settle swaps for multiple users in a single transaction using signed permits + +Permit2 is live on Tempo and supports smart contract accounts and EOAs owned by ECDSA keys, but it is unable to serve EOAs owned with P256 or Webauthn keys. Permit2 also requires a pre-existing approval. + +--- + +# Specification + +This TIP extends TIP-1004. All behavior defined in TIP-1004 (EIP-712 typed data, domain separator, permit typehash, nonces, deadline, pause state, TIP-403 transfer policy) remains unchanged. The new `bytes signature` overload shares the same EIP-712 domain, the same `Permit` typehash, and the same nonce space as the existing `(v, r, s)` permit — the two overloads are interchangeable from the signer's perspective. + +## New function + +The following function is added to the TIP-20 interface alongside the existing TIP-1004 `permit(address,address,uint256,uint256,uint8,bytes32,bytes32)`: + +```solidity +/// @notice Approves `spender` to spend `value` tokens on behalf of `owner` via a signed permit +/// @param owner The address granting the approval +/// @param spender The address being approved to spend tokens +/// @param value The amount of tokens to approve +/// @param deadline Unix timestamp after which the permit is no longer valid +/// @param signature The signature bytes +/// @dev The permit is valid only if: +/// - The current block timestamp is <= deadline +/// - The signature is valid and was signed by `owner`, either via ecrecover (for 65-byte +/// ECDSA signatures) or via EIP-1271 isValidSignature (for smart accounts and +/// non-secp256k1 keys) +/// - The nonce in the signature matches the current nonce for `owner` +/// Upon successful execution, increments the nonce for `owner` by 1. +/// Emits an {Approval} event. +function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + bytes calldata signature +) external; +``` + +## Signature Encoding + +For EOAs, the `signature` parameter must be encoded using the formats defined in [TIP-1020](./tip-1020.md#signature-encoding): + +| Type | Format | Length | +|------|--------|--------| +| secp256k1 | `r \|\| s \|\| v` | 65 bytes | +| P256 | `0x01 \|\| r \|\| s \|\| x \|\| y \|\| prehash` | 130 bytes | +| WebAuthn | `0x02 \|\| webauthn_data \|\| r \|\| s \|\| x \|\| y` | 129–2049 bytes | + +Keychain signatures (type `0x03`) are not supported by `permit()`. + +For EIP-1271 smart contract wallets, the `signature` bytes are passed through to `isValidSignature` as-is, so the encoding will be wallet-defined. + +## Signature Validation Implementation + +The implementation must: +1. Verify that `block.timestamp <= deadline`, otherwise revert with `PermitExpired` +2. Retrieve the current nonce for `owner` and use it to construct the `structHash` and `digest` (per TIP-1004) +3. Increment `nonces[owner]` +4. Validate the signature: + - Calls `verify` on the TIP-1020 signature verification precompile passing in the signature and the digest. + - If the call is unsuccessful, the account is not any Tempo EOA. + - Check if the `owner` has code. If so, call `owner.isValidSignature(digest, signature)` per EIP-1271. + - If `isValidSignature` returns the magic value `0x1626ba7e`, the signature is valid + - Otherwise, revert with `InvalidSignature` +5. Set `allowance[owner][spender] = value` +6. Emit an `Approval(owner, spender, value)` event + +# Invariants + +All invariants from TIP-1004 continue to hold. Additionally: + +- Both permit overloads must share the same nonce space — calling either overload increments the same `nonces[owner]` counter +- A permit signed for the `(v, r, s)` overload must also be valid when submitted as a 65-byte `bytes signature` (and vice versa) +- EIP-1271 validation must use `staticcall` — it must not modify state + +## Test Cases + +In addition to all TIP-1004 test cases, the following must be covered: + +1. **Equivalence with TIP-1004 for ECDSA EOAs**: Any permit call that succeeds for TIP-1004 should succeed when calling the new permit overload. +2. **P256 and Webauthn EOA**: Permit succeeds with P256 and Webauthn EOA signatures and sets allowance to permit amount. +3. **EIP-1271 smart contract wallet**: Permit works with smart contract wallet that implements `isValidSignature`. +4. **EIP-1271 rejection**: Reverts with `InvalidSignature` if smart contract wallet returns wrong magic value, or if the call reverts, or if the call returns values that are not a `bytes4`. +5. **Cross-overload nonce sharing**: Using `(v, r, s)` permit increments nonce, subsequent `bytes signature` permit must use the new nonce. +6. **Verification should check EOA then smart account**: EIP-1271 isValidSignature should not happen if EOA verification is successful. \ No newline at end of file