ERC-8114: NFT Transfer With Signature

This proposal extends smart contract wallets with an off-chain signature flow for NFT transfers, allowing wallet owners to delegate transfers via typed-data signatures presented by any relayer. The extension defines a canonical EIP-712 schema, nonce accounting, and execution requirements for compliant implementations. Unlike NFT-native standards, this extension operates at the wallet level, enabling gasless transfers for any ERC-721 NFT without requiring NFT contract modifications.

Motivation

Wallets require on-chain transactions for every NFT transfer. This creates friction for gasless use cases such as gifting, claimable drops, custodial listings, and relayer-sponsored transfers.

This extension enables gasless transfers for all ERC-721 NFTs managed by the wallet, significantly expanding the reach of gasless NFT applications.

A standardized off-chain transfer signature reduces bespoke integrations and paves the way for widespread adoption of NFT-based interactions in Web3.

Specification

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119 and RFC 8174.

Compliant smart contract wallets that wish to support this extension MUST implement the IERC8114 interface (in addition to ERC-165 for interface detection and ERC-1271 for signature validation).

Interface

interface IERC8114 {
    function nftTransferNonce(address asset, uint256 tokenId) external view returns (uint256);

    function nftTransferWithSig(
        address asset,
        uint256 tokenId,
        address to,
        uint256 deadline,
        bytes calldata signature
    ) external returns (bool success);
}
  • nftTransferNonce MUST return a monotonically increasing nonce scoped to (asset, tokenId).

  • nftTransferWithSig MUST verify the signature using isValidSignature, reject expired signatures (unless deadline == 0), verify the nonce, increment the nonce before the transfer, and execute IERC721(asset).safeTransferFrom(address(this), to, tokenId).

Implementations MUST support wallets that validate signatures through ERC-1271. Wallets MAY wrap signatures but the module MUST unwrap them prior to verification.

Typed Data

Compliant implementations MUST use the following EIP-712 typed data structure. The wallet field identifies the smart contract wallet that will execute the transfer.

Function Primary Type Fields
nftTransferWithSig NFTTransferWithSig wallet, asset, to, tokenId, nonce, deadline

Every permit MUST use the EIP-712 domain:

  • name = "ERC8114 NFT Transfer"

  • version = "1"

  • chainId equal to the executing chain

  • verifyingContract = address(this)

Nonce Semantics

  • nftTransferNonce MUST be scoped per (asset, tokenId).

Each WithSig function MUST increment its nonce immediately before state changes and MUST revert on signature reuse.

Execution Requirements

Implementations MUST:

  1. Treat deadline = 0 as non-expiring; otherwise enforce block.timestamp <= deadline.

  2. Recover the signer from the EIP-712 digest. Validate the digest using ERC-1271 on the wallet address. The recovered/validated signer MUST be authorized to act on behalf of the wallet (typically its owner).

  3. Verify the provided nonce matches the current stored nonce for the corresponding scope.

  4. Increment the scoped nonce before invoking the underlying ERC-721 function. The nonce MUST remain incremented even if the downstream NFT transfer call reverts.

  5. Revert if signature validation fails, inputs mismatch, or deadlines are exceeded.

Wallets MAY offer helper wrappers, but they MUST NOT alter the semantics described above.

Rationale

  • Reusing the existing ERC-721 entry points keeps events and accounting compatible with current deployments.

  • This wallet-level approach supports gasless transfers for all ERC-721 NFTs, including those without native off-chain authorization mechanisms.

Backwards Compatibility

Clients can detect permit support by checking for IERC8114 via ERC-165 or probing the new function selectors.

2 Likes

I like the direction of this proposal. Handling NFT transfer authorization at the wallet level instead of the NFT contract itself feels like the right abstraction, especially if the goal is to make gasless NFT interactions broadly usable without fragmenting standards.

A few points and questions from an implementation perspective:

Why this approach makes sense

  • Wallet-level authorization scales better than NFT-level permits
    By extending ERC-7564 wallets instead of touching ERC-721 contracts, this works for all NFTs, including legacy collections. That’s a big practical win.
  • Relayer-friendly without compromising security
    Using EIP-712 + ERC-1271 keeps the trust model clean. The wallet owner authorizes intent, anyone can relay, and the wallet remains the final enforcer.
  • Good UX foundation for gasless flows
    Gifting, claimable drops, and custodial or mobile-first wallets all benefit from this without forcing users to hold ETH.

Questions / considerations

  • Nonce scoping (asset, to)
    What was the reasoning behind scoping nonces per (asset, to) instead of (asset, tokenId) or a global wallet nonce?
    From an integrator’s view, this choice impacts how parallel authorizations and batching are handled.
  • Partial replay risk across destinations
    Since the nonce is destination-scoped, signing the same NFT for different to addresses would require separate signatures. This seems intentional, but it might be worth clarifying as a UX trade-off in the spec.
  • Multisig / modular wallet flows
    For wallets with delayed or aggregated ERC-1271 validation (e.g. multisig thresholds or session keys), are there any known edge cases around deadline enforcement or nonce invalidation that implementers should watch for?

This feels like a pragmatic and composable extension to ERC-7564 rather than a competing NFT transfer standard. If standardized cleanly, it could remove a lot of bespoke “gasless NFT” logic currently being reimplemented at the app layer.

2 Likes

Thanks, this is helpful feedback.

On nonce scoping: I chose (asset, to) because this extension treats transfer authorization as destination-bound intent.
tokenId is already in the signed payload, so NFT specificity is preserved, while destination-scoped nonces fit wallet-level gift / claim / relayer flows better. A global nonce would create more unrelated contention; (asset, tokenId) would simply optimize for a different model.

On the “same NFT, different destinations” case: yes, that is intentional. Changing to means changing the authorized transfer, so it requires a different signature. I agree this should be stated more explicitly.

On multisig / modular ERC-1271 wallets: I do not see a special incompatibility. The main requirement is that deadline checks, nonce handling, and ERC-1271 validation all happen in the same execution path. How the wallet decides whether a signature is authorized remains wallet-specific.

Makes sense, the destination-bound intent with (asset, to) is a reasonable tradeoff for relayer-driven flows.

One thing I’d still be curious about is how this behaves under high parallelism. With (asset, to) scoping, concurrent transfers of different tokenIds to the same destination will still contend on a single nonce, which might limit batching efficiency for some use cases.

Not necessarily a blocker, but might be worth calling out as a tradeoff in the spec so integrators can design around it.

1 Like

Thanks, this was a useful question.

I’ve updated the design since the earlier draft. For NFTs, I no longer scope the nonce to (asset, to). The current design uses a nonce scoped per (asset, tokenId), because for ERC-721 the token itself is the natural unit of replay protection. That gives cleaner semantics: once one signed transfer for a given NFT is executed, other signatures for that same NFT become invalid.

I also updated the proposal so ERC8114 is not tied to ERC-7564 anymore. It can be added to any smart contract wallet that supports ERC-1271-style signature validation. ERC-7564 compatibility is still useful for execution semantics, but ERC8114 itself does not depend on ERC-7564.