TIP-4494: TRC-721 Permit

TIP-4494: TRC-721 Permit (Gasless NFT Approval)

Summary

Add a permit function to the TRC-721 (NFT) token standard, allowing NFT owners to approve the transfer of their tokens via an off-chain signature instead of an on-chain approve transaction. This gasless approval mechanism lets a user sign a permission (following the EIP-712/TIP-712 structured data format) that a third party can then submit on-chain, so the user does not have to pay TRX for an approval step. This improves Tron dApp user experience by removing the need for a separate approval transaction when granting NFT transfer allowances.

Abstract

This proposal introduces an extension to TRC-721 (Tron’s ERC-721 equivalent) analogous to Ethereum’s ERC-4494 permit standard. It defines a new function, permit, along with associated read-only functions nonces and DOMAIN_SEPARATOR, that NFT contracts can implement to enable signed approvals. Using permit, an NFT holder signs a message authorizing a spender (an address) to be approved for a specific token ID (with a deadline), and any actor can then call permit(tokenId, spender, deadline, sig) on the token contract with that signature to set the approval – without the token owner themselves sending a transaction. The signature is verified in the smart contract using the ECDSA ecrecover primitive against a structured message per TIP-712 (Tron’s implementation of EIP-712 for typed data signing). If the signature is valid and not expired, the contract updates the token’s approved address accordingly (just as if the owner had called the standard approve) and emits an Approval event, then invalidates the permit by incrementing a nonce for that token ID. This ensures each permit signature can only be used once, preventing replay attacks. The result is a “gasless” NFT approval flow where a relayer or marketplace can pay the network fees on the user’s behalf, simplifying NFT transfers and listings by eliminating the extra approval transaction.

Motivation

Why add a permit function to TRC-721?

In the current TRC-721 standard (which mirrors Ethereum’s ERC-721), approving a spender or marketplace to transfer your NFT requires a separate on-chain transaction (approve or setApprovalForAll) initiated by the token owner. This has several drawbacks in practice:

  • Usability: New or casual users must acquire TRX to pay gas or bandwidth for an approval, even if they only intend to use, for example, a collectible or game item NFT. This extra step creates friction. As with ERC-20 tokens, the normal “approve, then transfer” flow involves two transactions which is poor UX and often confuses or deters users. A permit function simplifies this by allowing a one-step transfer (the user’s signed permit is used in the same transaction that transfers the NFT), improving onboarding for users who may not hold TRX.
  • Efficiency: The two-step process of approve-then-transfer means additional network overhead and potential delays. A user might pay for an approval transaction that ends up being unnecessary (e.g., if a sale or transfer never happens). With a permit, the approval signature is only used at the moment of the actual transfer/listing execution, so the user avoids upfront costs for approvals that might not be utilized. This on-demand usage of approvals makes interactions more efficient – the gas (or energy) cost is incurred only when the NFT is actually transferred, and that cost can be covered by the party executing the transfer (such as a buyer or relayer), not the original owner.
  • Marketplace UX: In Tron NFT marketplaces (analogs of OpenSea and others), sellers currently have to send an on-chain approval transaction to list an NFT for sale (granting the marketplace contract permission to transfer the NFT). This extra step not only costs TRX but also complicates the selling process. By using a permit mechanism, a seller could instead sign an off-chain message giving the marketplace contract permission to transfer a specific NFT upon sale. The marketplace (or buyer) would then submit this permit with the purchase transaction, completing the sale in one go. This eliminates the need for a “approve NFT then list” sequence; the listing can be gasless for the seller. Such “gasless listings” greatly improve user experience, as the seller does not spend TRX to list their item – the approval is bundled with the eventual transfer and paid by the buyer. This model has been proposed and implemented on Ethereum via EIP-4494 for more seamless NFT trading, and bringing it to Tron would similarly streamline NFT marketplaces.
  • Parity and Innovation: Tron has already embraced the concept of gasless token approvals for TRC-20 tokens (fungible tokens) with its adoption of a permit-style standard (analogous to Ethereum’s EIP-2612) for assets like USDT. Extending a similar capability to non-fungible tokens keeps Tron’s token standards up-to-date with the latest developments in Ethereum and other chains. It provides a consistent developer experience – dApp developers on Tron can leverage off-chain signatures for both fungible and non-fungible tokens. Additionally, it would enable new use cases on Tron, such as meta-transactions and relayer services for NFTs, and foster a more user-friendly NFT ecosystem. TRC-721 is fully compatible with ERC-721 but currently lacks any permit function for off-chain approvals, so this TIP would fill that gap. With TIP-712 standardized on Tron for signing structured data, the infrastructure is in place to support EIP-712 style permits on Tron. Implementing permit in TRC-721 contracts (and possibly standardizing it in the TRC-721 interface) would allow Tron to support gasless NFT transfers and listings, boosting its appeal for NFT applications and marketplaces.

Status on Tron: TRC-721 interface only includes the traditional on-chain approval methods (approve and setApprovalForAll). This proposal suggests adding the permit extension (as defined above) to Tron’s NFT standard. Doing so would require supporting EIP-712-compatible signatures (via TIP-712) and updating the standard interface, but it would bring significant UX improvements by enabling gasless approvals for NFTs on Tron, similar to Ethereum’s EIP-4494 permit mechanism. The ability for users to authorize NFT transfers with a signed message rather than a transaction can make Tron-based NFT platforms more accessible and competitive, allowing features like gasless listings and one-click purchases that improve overall user experience.

Specification

The TRC-721 Permit standard extends TRC-721 with a new permit capability. It introduces three new functions to the NFT contract interface, following the model of TRC-20 Permit (TIP-2612) but adapted for non-fungible tokens:

  • function permit(address spender, uint256 tokenId, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
    Allows the owner of an NFT to approve a spender via an off-chain signature, instead of an on-chain transaction. The deadline is a timestamp (in seconds) after which the permit is invalid.
  • function nonces(uint256 tokenId) external view returns (uint256);
    Returns the current nonce for the given NFT (tokenId). Each NFT has its own nonce used to prevent signature reuse.
  • function DOMAIN_SEPARATOR() external view returns (bytes32);
    Returns the EIP-712 domain separator for the contract, as defined in TIP-712 (Tron’s structured data signing standard).

These functions form the TRC-721 Permit interface. The interface identifier for this extension (for TRC-165 detection) is 0x5604e225, matching the Ethereum EIP-4494 interface ID. Contracts implementing TRC-721 Permit MUST return true for supportsInterface(0x5604e225) to indicate support.

The permit mechanism uses an EIP-712 typed data signature (per TIP-712) authorizing a spender for a specific NFT. The signed message (structured as a Permit type) includes the spender address, the NFT tokenId, the token’s current nonce, and the deadline. The EIP-712 domain separator is unique to the contract and network chain. In Tron, this follows TIP-712, meaning the domain uses the contract’s name, version, its address, and the Tron network’s chain ID, with Tron-specific adjustments (the Tron address 0x41 prefix is removed when hashing an address, and the chainId is masked to 32 bits for the domain). The signature is produced by the NFT owner off-chain and later provided to the permit function call.

Calling permit(spender, tokenId, deadline, v, r, s) on a TRC-721 contract will internally verify the signature and set an approval if valid. Specifically, the implementation MUST perform the following checks and actions (no involvement of msg.sender is required, anyone can submit the permit):

  • The current blockchain timestamp must be <= the provided deadline (i.e. the permit has not expired).
  • The ownerOf(tokenId) must be a valid address (not the zero address) and this is the address that should have signed the permit.
  • nonces(tokenId) (the current nonce for the NFT) must equal the nonce value that was included in the signed permit message.
  • Using ecrecover, the signature (v,r,s) must recover the address of the current owner of tokenId. The recovered signer must equal ownerOf(tokenId) to prove the permit was signed by the NFT’s owner.

If and only if all the above conditions are satisfied, the contract MUST set spender as the approved address for tokenId (equivalent to calling approve(spender, tokenId) internally) and MUST emit the standard Approval(owner, spender, tokenId) event. This effectively grants the specified spender the right to transfer the NFT via transferFrom or safeTransferFrom, without the owner having to call approve themselves. The permit function itself does not transfer the NFT; it only sets an approval.

In addition, the implementation must adhere to the following to ensure security and compatibility:

  • The signature’s signer (recovered owner) must not be the zero address. An invalid or malformed signature causes ecrecover to return address(0), which should be treated as an invalid permit and result in a failure (revert).
  • The nonces(tokenId) value MUST NOT be incremented when permit is called. Unlike fungible token permits, an ERC721 permit does not consume the nonce on use. This allows an owner to create multiple distinct signatures (permits) for the same token without immediately invalidating previous onessoliditydeveloper.comdocs.immutable.com. However, the nonce must be incremented whenever the NFT is transferred to a new owner. Incrementing on transfer ensures that once the token changes ownership, any permits signed by the previous owner can no longer be used (replay protection across transfers). In practice, the nonce is tied to a given token’s ownership and lifecycle: when tokenId moves to a new owner, nonces(tokenId) is bumped by the contract (typically in the _beforeTokenTransfer or _transfer hook) to invalidate prior signatures.

TIP-712 Considerations: Because Tron addresses include a 0x41 prefix, the implementation should remove this 1-byte prefix when hashing any address as part of the EIP-712 encoding (addresses are treated as 160-bit values in the signed data). The Tron chain ID used in the domain separator should be taken as block.chainid & 0xffffffff (the low 32 bits of the chain’s identifier) per TIP-712. Aside from these Tron-specific adjustments, the signing and verification follow the standard EIP-712 and secp256k1 curve rules, meaning the signed permit message hash is computed as: keccak256(0x19||0x01||DOMAIN_SEPARATOR||hashStruct(Permit)), and the signature is validated with ecrecover. Wallets and libraries must format the permit message according to Tron’s TIP-712 so that the user’s wallet (e.g. TronLink or other Tron signing tool) displays the permit request clearly and produces a correct signature.

Differences from Ethereum’s EIP-4494

While this proposal is based on Ethereum’s EIP-4494: ERC-721 Permit, there are a few important differences in the Tron context:

  • Address Format: Tron addresses include a 21-byte format (with a leading 0x41 byte) when represented in hex or Base58. For the purposes of signing and verifying the permit’s EIP-712 message, the 0x41 prefix byte is removed so that the address is treated as a standard 160-bit value. In other words, the owner and spender addresses in the permit struct are the 20-byte account addresses (as used within the TVM), not the 21-byte Base58Check representations. This is a Tron-specific adjustment to ensure the EIP-712 hashing is consistent with Ethereum’s address hashing.
  • Chain ID in Domain: Tron’s chain identifier is different from Ethereum’s and may be a large number. TIP-712 specifies that the chainId used in the EIP-712 domain separator should be block.chainid & 0xffffffff. Therefore, the TRC-721 permit domain separator uses a 32-bit masked chain ID. This prevents issues if Tron’s native chainid doesn’t fit in 256 bits or to reduce risk of cross-chain replay. On Ethereum, EIP-4494 uses the full chainId in the domain by default. Tron developers must ensure they use the masked chain ID value when constructing the domain separator for permits.
  • Signature Encoding: In EIP-4494, the permit function is defined to take a single bytes parameter for the signature, allowing support for both 65-byte (r,s,v) signatures and the 64-byte compact signatures defined in EIP-2098. In this Tron TIP, for consistency with TIP-2612 (TRC-20 Permit) and existing Tron conventions, the permit function is defined with separate v, r, s parameters. This means wallets will typically produce a standard 65-byte signature which is then split into v,r,s when calling the function. Tron contracts may still support EIP-2098-formatted signatures if desired (by reconstructing v,r,s from a 64-byte input internally), but the standard interface here is given in the expanded form. The difference is only at the interface level – the underlying cryptography (secp256k1 signatures per EIP-712) remains the same.
  • Nonce Usage: EIP-4494’s design uses token-specific nonces that are not incremented on permit calls (only on transfers). This Tron standard follows the same design. The implication is that an NFT owner can sign multiple permits for the same tokenId (for different spenders or use cases) using the same nonce, and any of those permits can be exercised until the token is transferred. Once a transfer happens, the nonce increases and all those signatures become invalid. This is a nuanced difference from ERC-20 permits (EIP-2612) where a nonce is consumed on each permit call. It is a design choice to accommodate NFTs, not a divergence between Tron and Ethereum, but worth noting for developers used to ERC-20 permit semantics.

Apart from the above, the TRC-721 Permit functions behave the same as in Ethereum’s EIP-4494. The goal is to maintain maximum compatibility: any software or workflow that works with ERC-4494 permits can be adapted to Tron with minimal changes, mainly accounting for Tron’s address format and domain separator differences. The permit message structure (types and fields) is identical to EIP-4494’s (spender, tokenId, nonce, deadline in an EIP-712 Permit struct), and the security considerations (signature verification, deadline enforcement, etc.) are equivalent.

Requires

This standard requires TIP-712: TRON Typed Structured Data Hashing and Signing, which is Tron’s equivalent of Ethereum’s EIP-712. TIP-712 provides the foundation for hashing and signing structured messages (like the permit) in a secure and user-friendly way. Tron client support for TIP-712 is essential so that wallets can correctly display the permit details and produce a valid signature for the Permit(address spender,uint256 tokenId,uint256 nonce,uint256 deadline) structure. In practice, this means that Tron wallets (such as TronLink or others) need to support prompting the user to sign a typed message conforming to the TRC-721 Permit schema, and Tron’s chainId must be correctly handled as described in TIP-712 when computing the DOMAIN_SEPARATOR. The TRC-721 Permit does not introduce any new cryptographic scheme beyond TIP-712 and ECDSA; it simply relies on that infrastructure being in place. Therefore, TIP-712 is a prerequisite for ecosystem adoption of gasless NFT approvals.

Backward Compatibility

TRC-721 Permit is designed as an extension to the existing TRC-721 standard. It is fully backward-compatible:

  • Optional Implementation: Existing TRC-721 contracts are not affected by this proposal unless they choose to implement it. A contract that doesn’t include the new permit functions continues to operate as before with no changes in behavior. There is no modification to the TRC-721 core methods or event definitions.
  • No Protocol Changes: This is a user-space (application-layer) standard. It does not require any changes to the Tron protocol or VM. The permit functionality is implemented purely in the smart contract code (and supported by wallets for signing). As noted by Tron developers, EIP-2612-style permits “do not involve changes to the protocol layer,” and the same holds for TRC-721 permits – they are simply contract code and conventions on top of Tron’s existing capabilities.
  • Coexistence with Approvals: The introduction of permit() does not disrupt the existing approval workflow. Contracts may still use approve() and setApprovalForAll() as defined in TRC-721. The permit function is an additional path to set an approval. Both methods of approval (on-chain transaction or off-chain signature) update the same internal state (the token’s approved address). If a contract implements both, they should interoperate safely. For example, if a permit is used to approve a spender, a later call to the standard approve() for the same token will simply overwrite it (or vice versa), as usual in TRC-721.
  • Interface Identification: Because this standard uses TRC-165 interface detection (supportsInterface), applications can dynamically check if a given NFT contract supports permits without assuming it. This means wallets and dApps built around TRC-721 Permit can fall back to the normal approve process if a particular contract does not implement the extension. Thus, adoption can be incremental.
  • No Impact on Non-Permit Contracts: There is no impact on contracts that choose not to implement this. The addition of a new optional function in other contracts does not break any existing TRC-721 assumptions. From the Tron network’s perspective, a permit call is just an external function call that will fail if not present; it doesn’t interfere with network rules or existing token operations.

In summary, TRC-721 Permit is an additive, non-breaking extension. It follows the precedent of TIP-2612 (which added permits to TRC-20) in ensuring that existing contracts and tools remain unaffected unless they opt in. This design encourages gradual adoption: new NFT projects can include permit functionality for improved UX, while older projects remain compatible with the ecosystem.

Example Workflow

To illustrate how gasless NFT approval works in practice, consider a scenario with an NFT marketplace:

  1. Off-chain Signature by Owner:
  • Alice owns an NFT (token ID 123) that she wants to list for sale on a Tron-based marketplace. Instead of sending an on-chain approve transaction (which would cost energy/TRX), Alice’s wallet (supporting TIP-712) generates a permit signing request. This request includes the marketplace’s address (to be approved as spender), the token ID 123, the current nonce for token 123 (say 0), and an expiry time (deadline).
  • Alice reviews the details and signs the permit message with her private key. This signature is produced off-chain, so Alice pays no fee to create it.
  1. Submitting the Permit (Relayer or Spender):
  • Alice then provides the signature data to the marketplace (for example, by pasting it into the site or via an automated wallet integration).
  • The marketplace (or a relayer service) now crafts a transaction to call the NFT contract’s permit(spender, tokenId, deadline, v, r, s) function. This transaction includes the signature Alice made.
  • The marketplace sends this transaction to the Tron network, paying the gas/energy cost for execution. Importantly, Alice does not spend any TRX in this step – the fee can be covered by the marketplace or the buyer.
  • The permit call is open to anyone, so a relayer can do it on behalf of Alice.
  1. On-chain Verification:
  • When the permit function runs in the NFT smart contract, it uses ecrecover to verify that the provided signature matches Alice’s address (the current owner of token 123) and that all parameters are valid. It checks that the signature has not expired (deadline not passed) and that the nonce is correct.
  • If any check fails, the transaction reverts (and the marketplace’s transaction will be aborted). If the checks pass, the contract sets the approved address for token 123 to be the marketplace’s address.
  • An Approval(Alice, marketplace, 123) event is emitted, just as if Alice had called approve(marketplace, 123) herself.
  1. NFT Transfer by Marketplace:
  • Now that the marketplace contract is approved to transfer token 123, a buyer can purchase the NFT. In the same transaction (or a subsequent one), the marketplace can call transferFrom(Alice, Bob, 123) (or the safer safeTransferFrom) to move the NFT from Alice to Bob, since it has approval. Because the marketplace is the approved spender, this transfer succeeds. Typically, the permit and the transfer can be bundled in one atomic transaction (the marketplace could call permit and then immediately call transferFrom within a multi-call or via the buyer’s interaction). This achieves the “approve and transfer in one transaction” flow that greatly simplifies user experience.
  1. Post-Transfer State:
  • Once the NFT is transferred to Bob, the contract will increment the nonce for token 123 (nonces(123) becomes 1). This invalidates the permit signature Alice gave (it can’t be reused on token 123 because the owner is no longer Alice and the nonce changed).
  • Bob is now the new owner. If Bob wants to sell or use a permit, he would have to sign a new permit as the owner, with the nonce value 1.

Throughout this process, Alice achieved an approval without ever directly spending gas on Tron. The gasless approval means the cost of setting up the approval was covered by someone else (the marketplace or buyer). This is especially powerful in scenarios like NFT marketplaces where many users might list items – they no longer need to pay a separate transaction fee just to authorize the marketplace.

Implementation

Implementing TRC-721 Permit in a smart contract is straightforward for developers familiar with EIP-712 and existing permit standards. The changes are confined to the contract code and do not require any special support from the Tron protocol beyond TIP-712 compliance.

  1. Developers need to add the nonces mapping (e.g., mapping(uint256 => uint256) private _nonces;) and the permit function to their TRC-721 contract. The DOMAIN_SEPARATOR can be set in the constructor (or via an immutable variable) using the contract’s name, version, Tron chain ID, and contract address, hashed according to EIP-712 (remembering to apply the Tron chain ID mask). The permit function itself should implement the logic described in the Specification: verify the deadline, owner, nonce, and signature, then call the internal _approve(spender, tokenId) function. The usual precautions from EIP-2612 and EIP-4494 reference implementations apply (e.g., check owner != address(0), ensure ecrecover success). After a transfer in the _transfer override, increment the nonce for that tokenId.
  2. Ethereum’s EIP-4494 has a reference implementation and several live examples (such as Uniswap V3’s use of signed NFT approvals). These can be used as a starting point. Tron developers can port these implementations with minimal changes – primarily adjusting the domain separator calculation for Tron and using Tron’s address handling. For instance, the reference code constructs the permit digest as per EIP-712 (0x1901 prefix, etc.); this logic can be directly used. The main difference is inserting Tron’s chain ID mask and stripping the 0x41 byte from addresses when hashing (which is usually handled by TIP-712 compliance in the hashing functions). Given that TIP-712 is totally compatible with EIP-712 except for those minor tweaks, Ethereum code for permit (solidity or Vyper) will function on Tron with those adjustments.
  3. With TIP-712 in place, Tron’s tooling (TronWeb, TronLink, etc.) should be able to support signing of the permit message. It’s expected that wallet libraries will incorporate TRC-721 Permit as a known domain/struct once the standard is finalized. For example, an application can use TronWeb or a similar library to request a user signature for the structured data (providing the domain fields and message as specified). As long as the wallet knows how to handle TIP-712 signing, the resulting signature is compatible with the permit function’s expectations.
  • Testing: Just as with Ethereum’s permits, thorough testing is needed. Test cases should cover valid permits, expired permits, wrong signer, replay attacks (using the same signature twice, which should fail the second time if the NFT was transferred or if nonce was somehow consumed), and ensuring that calling permit with an incorrect owner signature does nothing. The Tron developers can reference Ethereum’s EIP-4494 test vectors and adapt them. Key scenarios include: signature from wrong owner (should fail), using a permit after transferring the NFT (should fail due to nonce mismatch), and using a permit right before and after the deadline.
  1. The implementation does not depend on any new opcodes or protocol features – it uses standard Solidity and Tron VM capabilities (keccak256, ecrecover which Tron supports, etc.). This means contracts with permit can be deployed on Tron just like any other contract. The Tron nodes/FullNodes do not need any update to support this; only wallets and dApps need to be aware to use it. This simplifies adoption: any project can start implementing TRC-721 permits right away in their contracts, and as wallet support catches up, users can start benefiting from gasless approvals.

Currently, this standard is in the proposal phase. Prototype implementations can be created to ensure everything works as expected on Tron (especially verifying that Tron’s chainid and address formats are handled correctly in signatures). Once finalized, we expect common Tron NFT contract libraries or templates to include permit. For example, an updated TRC-721 base contract could integrate this natively, making it easy for developers to opt-in by inheriting the permit functionality.

Conclusion

In summary, TRC-721 Permit enhances the TRC-721 standard by adding an optional, backwards-compatible method for approval via signature, improving efficiency and user experience in many scenarios (marketplace listings, NFT rentals, gaming item transfers, etc.). We encourage the Tron developer community to review this TIP, provide feedback, and consider implementing it in new NFT contracts. With wallet and dApp support, TRC-721 Permit can become a widely used standard that benefits all participants in the Tron NFT ecosystem. The next steps would be community review, reference implementations, and integration into popular tooling. By adopting this standard, Tron dApps can offer gasless approvals and smoother workflows, making Tron a more attractive platform for NFT trading and utilization. We are welcome discussion and aim for the community to embrace TRC-721 Permit as a new pillar of the Tron token standards.

5 Likes

I already shared my support for TIP-2612 (TRC-20 Permit), and I’m equally in favor of adding the same permit pattern to TRC-721. +1.

1 Like

Does this proposal include a permitAll function similar to setApprovalForAll, or is it strictly for individual Token IDs?

Love this proposal. Regarding the nonces mapping: is it possible to check the current nonce of a specific NFT through a public constant function, or is it managed at the contract level for the whole user address?

Nice proposal. How does the proposed permit function interact with existing TRC-721 contracts that use different approval logic (e.g., with operator roles)? Is there a migration path or a wrapper pattern recommended for already deployed collections?

Great to see TRC-712 structured data being used here. My main concern with off-chain permits for high-value NFTs is always ‘signature phishing’. If a user signs a permit for a malicious actor, they could lose their NFT without ever sending an on-chain transaction. Does the proposal suggest any UI/UX standards for wallets to make sure the permit details (like the specific TokenID and Spender) are clearly visible to the user before they sign?

Really interesting to see TRC‑721 adopting permits. From a developer standpoint, this should reduce friction for NFT interactions and improve UX across wallets. One thing I’d love more detail on: how should dApps handle revocation of permits if a user wants to cancel an approval? Since NFTs often carry high-value assets, having a clear, trustless revocation mechanism could be critical for both marketplaces and custodial services.