TIP-2612: TRC-20 Permit (Gasless Token Approval)
Summary
Add a permit function to the TRC-20 token standard, allowing token owners to approve spending 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 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 token allowances.
Abstract
This proposal introduces an extension to TRC-20 (Tronâs ERC-20 equivalent) analogous to Ethereumâs EIP-2612 permit standard. It defines a new function, permit, along with associated read-only functions nonces and DOMAIN_SEPARATOR, that token contracts can implement to enable signed approvals. Using permit, a token holder signs a message authorizing a spender and amount (with a deadline), and any actor can then call permit on the token contract with that signature to set the allowance, 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 EIP-712 implementation). If the signature is valid and not expired, the contract updates the allowance accordingly and emits an Approval event â just as if the owner had called approve â then invalidates the signature by incrementing a nonce. This enables âgaslessâ approvals where a relayer or dApp can pay the network fees on the userâs behalf, simplifying DeFi interactions by eliminating the extra approval transaction.
Motivation
Why add a permit function to TRC-20?
In the current TRC-20 standard (which mirrors Ethereumâs ERC-20), approving a spender to use your tokens requires a separate on-chain transaction (approve) initiated by the token owner. This has several drawbacks in practice:
- Usability: New users must acquire TRX to pay gas or bandwidth for an approval, even if they only intend to use, say, USDT or another token. This âtwo-transactionâ hurdle (approve + the actual token usage) complicates onboarding. On Ethereum, a user needs to make two transactions (approve and then perform action) and hold ETH for gas; Tron reduces fees but still requires an approval step and TRX for contract energy or bandwidth. Removing the approval transaction lowers friction for users who may not hold TRX.
- Efficiency: Many DeFi use cases (DEX trades, yield farming deposits, etc.) involve a pattern where a user must first call
approveand then call a contract function (transferFromis invoked internally). With permit, these can be combined â the user signs a message off-chain and the dApp can submit the permit and the action in one go. This reduces the number of blockchain interactions, improving throughput and user flow. In short, fewer transactions means a smoother and faster UX. - Composability: A signed approval can be handed to a smart contract or relayer, enabling advanced meta-transaction workflows. For example, a dApp can accept a permit signature and immediately pull tokens from the userâs wallet in the same transaction that performs a swap or add-liquidity operation. This unlocks âone-clickâ interactions in Tronâs DeFi ecosystem, similar to Ethereumâs, without requiring the user to pre-approve tokens in a separate step.
- Parity with Ethereum DeFi standards: EIP-2612 (permit) has been widely adopted by Ethereum projects to improve user experience. By adopting an analogous standard, Tron makes it easier to integrate cross-chain applications and for developers to port Ethereum contracts to Tron. Many tokens and contracts expect the presence of a
permitfunction for gasless approvals; having a Tron standard for this ensures Tronâs ecosystem remains up-to-date with DeFi innovations.
Current status: TRC-20 standard currently does not include any permit functionality â it only provides the same basic methods as ERC-20 (transfer, approve, transferFrom, etc.). Thus, Tron tokens issued before 2020 (when permit was introduced in Ethereum) have no built-in support for off-chain approvals.
By standardizing permit at the TRC-20 level, we ensure a consistent approach that wallet providers can support and dApp developers can rely on. This proposal, in conjunction with TIP-712 (Tronâs typed data signing standard), would allow Tron wallets (e.g. TronLink) to present users with a structured signing request (showing the spender, amount, deadline, etc.) and produce a signature that tokens can verify. The end result is that a Tron user could approve a token spend without ever explicitly paying gas for the approval, streamlining interactions especially in scenarios like decentralized exchanges, lending platforms, and sponsored transactions (where a third-party covers fees).
Specification
New Interface Extensions
A TRC-20 contract that supports permits MUST implement the following new functions (in addition to the standard TRC-20 methods):
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v, bytes32 r, bytes32 s
) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) â Allows owner to approve spender to spend up to value tokens on their behalf, via a signature. The deadline is a timestamp (in seconds) by which the permit must be submitted (to prevent indefinite reuse of the signed approval). v, r, s are the ECDSA signature components from the ownerâs signed permit message. This function does not require owner to be the caller; any account (typically, the spender or a relayer) can call permit with a valid signature from owner. If the signature is valid and the conditions (explained below) are met, the contract will set allowance[owner][spender] = value and emit an Approval(owner, spender, value) event, exactly as a successful approve call by owner would. The call then increments ownerâs nonce to prevent reuse of the signature. If the signature is invalid or any condition fails, the call must revert with no state changes.
nonces(address owner) â Returns the current permit nonce for owner. This nonce is included in the signed data each time owner produces a permit signature. Each successful call to permit increments the nonce for that owner. This ensures every permit signature can be used at most once. (The initial nonce for an owner, before any permits, is 0.)
DOMAIN_SEPARATOR() â Returns the EIP-712 domain separator value used in the permit signature scheme for this token contract. This is a constant or precomputed value specific to the contract and the chain, and is part of the hash that the owner signs (per EIP-712/TIP-712). The domain separator typically encodes the token contractâs name, version, the Tron network chain ID, and the contractâs address. Its purpose is to bind the signature to this particular token contract and blockchain, preventing replay of permits on forks or other tokens.
Permit Signature and Verification
The core of this standard is the signing and verification of a structured message, as defined by TIP-712 (Tronâs adaptation of EIP-712 for typed data). When an owner wants to authorize a spender via permit, they must produce an EIP-712 compliant signature on the following Permit data structure (which the contract will validate):
- Domain: The EIP712 domain for the token, which should include:
name: the tokenâs name (e.g., âMyTokenâ),version: the tokenâs version or a fixed string (e.g., â1â),chainId: Tronâs chain ID for the current network (note: Tron uses a 32-bit chain ID asblock.chainid & 0xffffffffper TIP-712),verifyingContract: the token contractâs address.
- Message (Permit struct):
owner: the token ownerâs address (the holder granting permission),spender: the address being authorized to spend the tokens,value: the token amount to approve (as auint256),nonce: the ownerâs current nonce (obtained fromnonces(owner)),deadline: the timestamp (as auint256) after which the permit is no longer valid.
These fields correspond exactly to the parameters of the permit function call and are hashed according to EIP-712 rules. In JSON-like notation, the EIP-712 typed data that the owner signs looks like:
{
"types": {
"EIP712Domain": [
{"name": "name", "type": "string"},
{"name": "version", "type": "string"},
{"name": "chainId", "type": "uint256"},
{"name": "verifyingContract", "type": "address"}
],
"Permit": [
{"name": "owner", "type": "address"},
{"name": "spender", "type": "address"},
{"name": "value", "type": "uint256"},
{"name": "nonce", "type": "uint256"},
{"name": "deadline", "type": "uint256"}
]
},
"primaryType": "Permit",
"domain": {
"name": <token name>,
"version": <token version>,
"chainId": <Tron chain ID>,
"verifyingContract": <token contract address>
},
"message": {
"owner": <owner address>,
"spender": <spender address>,
"value": <value to approve>,
"nonce": <owner's current nonce>,
"deadline": <deadline timestamp>
}
}
The token contract, upon permit being called, must perform the following steps to verify and apply the permit:
- Check that
block.timestamp <= deadline(the permit has not expired). If the current time is past the deadline, the permit is invalid. - Retrieve the current nonce for
owner(e.g.,n = nonces[owner]). Check that it matches thenoncevalue provided in the function call (and in the signed message). If the nonce does not match, the signature is not valid (it might be a replay or out-of-date permit). - Using the EIP-712 encoding rules, compute the hash of the Permit structure and the domain separator. In pseudocode, the expected hash (as per EIP-2612) is:
bytes32 digest = keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
keccak256(abi.encode(
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"),
owner,
spender,
value,
nonce,
deadline
))
)
);
Tron specifics: When encoding the owner and spender addresses for hashing, the implementation must ensure they are treated as 160-bit values. Tron addresses are typically presented with a T... base58 format or as 21-byte hex (including the 0x41 prefix) â the 0x41 byte prefix should be removed for the purpose of hashing and treated like a normal 20-byte Ethereum address. The chainId used in the domain separator should correspond to the Tron networkâs ID (e.g., 0x2b6653dc for mainnet, which is larger than Ethereumâs 32-bit IDs; TIP-712 dictates using the low 32 bits of block.chainid for compatibility). By following TIP-712, the above digest will be computed in a way consistent with Tronâs signing rules and Ethereumâs EIP-712, making it possible for wallets to sign the same structured data and for the contract to reconstruct the hash.
- Using the standard
ecrecoverfunction (Ethereum precompile 0x01, available in TVM), recover the signerâs address from the hashdigestand the signature(v, r, s). Letsignerbe the recovered address. This step checks the cryptographic validity of the signature. - Check that
signer == ownerfrom the function arguments. Also ensure thatowner(and thussigner) is not the zero address (to prevent an edge case whereecrecovercould return address(0) on malformed input, which might otherwise allow an approval for the zero address). If the recovered signer does not match the providedowneraddress, the signature is invalid. - If all the above conditions are satisfied, set
allowance[owner][spender] = value(overwriting any previous allowance for this pair) and emit anApproval(owner, spender, value)event to log the approval. Then incrementnonces[owner]by 1. The incremented nonce ensures this exact signature cannot be reused. (If any condition fails, the contract must revert without adjusting any allowances or nonces).
Crucially, the permit function does not require msg.sender == owner. The Ethereum EIP-2612 design explicitly notes that permit makes no use of msg.sender. This means a contract or another user can call permit as long as they have a valid signature from the token owner. In practice, a common pattern is that the spender (or a dedicated relayer service) will gather the signature from the owner off-chain (e.g., via a wallet prompt) and then call permit on-chain, paying the gas to set the allowance. After that, the spender is authorized to call transferFrom to transfer the tokens.
Calling
permitconsumes roughly the same amount of energy/gas as a standard ERC-20approve(plus the cost of an ecrecover precompile). In Tron, this can be covered by the caller (relayer or spender), or possibly by the contractâs energy if using Tronâs sponsor energy feature. The key point is the token owner doesnât need to initiate or pay for this call.
Differences from Ethereumâs EIP-2612
The specification described is deliberately as close as possible to Ethereumâs established standard. Tronâs implementation should remain compatible in spirit and logic with EIP-2612, with only the necessary adaptations for Tronâs platform:
- Address format: Tron addresses have a different representation (base58 or hex with 0x41 prefix) but are ultimately comparable to Ethereumâs 20-byte addresses. The hashing of addresses in the permit struct must ignore Tronâs
0x41prefix and use the 160-bit address value. This ensures the signature produced by wallets (which will likely conform to TIP-712 encoding) matches what the contract expects. - Chain ID differences: Tronâs chain ID may not fit in 32 bits (Tron uses a larger identifier internally). TIP-712 specifies using
block.chainid & 0xfffffffffor the domain separator. This means the domainâschainIdfield will be a 32-bit integer corresponding to the Tron network. For Tron MainNet, for example, if the full chain ID is a 64-bit or 128-bit value, the contract should use the lower 32 bits in the domain. The goal is to match whatever value Tron wallets (TronLink, etc.) use when signing typed data forchainIdâ by adhering to TIP-712, we ensure consistency. - TRC-20 naming: In Ethereumâs EIP-2612, the domain separator typically includes the human-readable token
nameand aversion. Tronâs TRC-20 tokens also have a name (and sometimes a version in their contract metadata). It is recommended to follow the same approach: include the tokenâs name and a version string in the domain to avoid collisions and clarify to the user what they are signing. (For example, if a token is called âJustStableâ, domain name = âJustStableâ, version = â1â.)
Other than these, the behavior of permit on Tron should mirror that on Ethereum. This ensures that developers familiar with ERC-20 permit will find TRC-20 permit identical in usage, and wallets can implement the signing UI with minimal changes (just adopting Tronâs chain ID and address format).
Requires
This proposal depends on Tronâs support for EIP-712 typed data signing. Notably, TIP-712 (TRON Typed Structured Data Hashing and Signing) has been finalized to provide this capability. Wallet providers and Tron tooling should implement TIP-712 so that wallets can create the correct Permit signature and domain separation. It also assumes the existence of the ecrecover precompile in TVM (which is available and used for verifying signatures in other contexts). No changes to the Tron protocol itself are required beyond what TIP-712 already provides.
Backward Compatibility
This TIP is designed as a non-breaking extension to the TRC-20 standard. Token contracts that do not implement permit are not affected â they continue to operate as normal. There is no change to the Tron protocol or the TVM; all changes are at the smart contract level.
For contracts that do implement permit according to this standard, the addition of the new functions does not interfere with any existing TRC-20 behavior. The state variables introduced (nonces, domain separator) are independent of the standard ERC-20 state (balances and allowances). The permit function itself is an external method that does not change any ERC-20 logic except via the allowed allowance update and event emission, which uses the same event (Approval) as a regular approval for compatibility. Any tooling that watches Approval events or calls allowance() will work the same with permits as it does with normal approvals â the difference is purely how the approval got initiated.
To take advantage of permits, wallets and dApps will need updates to support the signing flow and function call:
- Wallets (like TronLink, Ledger, etc.) should implement TIP-712 if they havenât already, and recognize the Permit typed data structure to allow users to sign it easily. This is a user-space improvement; if a wallet doesnât support it, users could still theoretically craft the signature via other means, but it would be impractical for mainstream use. The expectation is that wallet updates will coincide with the adoption of this TIP, as was the case on Ethereum where wallet support followed the standardâs finalization.
- Front-end libraries (TronWeb, TronLink JavaScript API, etc.) may provide convenience methods to generate the correct data to sign (for example, analogous to Ethereumâs
signTypedDatawith the proper domain for a given token). TronWeb already introduced functions like_signTypedDataandverifyTypedDataunder TIP-712, which can be used to implement the permit flows in dApps.
Existing TRC-20 tokens deployed before this TIP will not have permit. If a project with an existing token desires permit functionality, they have a few options:
- Deploy a new token contract that includes
permit(not always feasible for established tokens due to migration issues). - If the token is behind an upgradeable proxy or controlled by a contract owner, they could upgrade the implementation to add the
permitfunctions (ensuring storage layout compatibility for addingnonces). - Wrap the old token in a new contract that adds permit (somewhat complex and not ideal).
In any case, this TIP doesnât directly provide retroactive permit to old tokens; it sets the standard for new tokens or upgrades.
The presence of permit, nonces, and DOMAIN_SEPARATOR should not conflict with any existing standard method names. permit is a new function name not used in TRC-20. nonces and DOMAIN_SEPARATOR are also unique. Contracts that implement this TIP can be detected by interfaces (similar to how ERC-2612 has an interface ID, though since these are not indexed events or have return types, an ERC165 interface ID could be defined for permit if needed, but thatâs optional and not part of this spec). For most purposes, compatibility means that contracts and tools that are not aware of permit will simply ignore it, whereas those that are aware can start using it.
Using permit shifts the resource cost of approval from the token owner to whoever submits the transaction. This is by design. It doesnât change overall resource usage on the network significantly â one transaction (permit) is still executed. Tronâs resource model allows contract callers to use their bandwidth or energy or even for contracts to sponsor execution; all those features work with permit calls as with any other transaction. There is no adverse effect on existing bandwidth/energy mechanics.
One subtlety: if an owner signs a permit giving a spender an allowance and that permit is executed, it will overwrite any prior allowance the owner had set for that spender (just as a regular approve would). This is expected behavior, not a bug. If the owner had already approved the spender, the permit can increase, decrease, or reset the allowance to a specific value. This is identical to calling approve normally (which in Tron/Ethereum standards replaces the old allowance with the new value). Contracts that rely on the allowance being a certain value should consider this, but again, itâs no different from a user calling approve on their own.
In conclusion, this TIP is backwards-compatible in the sense that it doesnât disrupt any existing functionality of Tron or TRC-20 tokens. It is an opt-in enhancement. Ecosystem software that knows about it can use it, while older software will continue working as before (just without the benefit of permits). As more tokens adopt permit, we expect wallets and dApps to naturally incorporate support.
Example Workflow
To illustrate the use of permit, consider the following example scenario:
- Token Setup: Alice holds 1000 units of a TRC-20 token that implements
permit. Initially, she has not approved any spender (allowance to Bob is 0). The current nonce for Alice (as given bynonces(Alice)) is 0. - Use Case: Bob runs a dApp (or a relayer service) that wants to move some of Aliceâs tokens (perhaps Alice is lending tokens in a protocol or trading on a DEX where Bobâs contract will pull her tokens). Alice is willing to authorize 250 tokens to Bobâs control, but she prefers not to pay the fee for an approval transaction.
- Alice Signs a Permit: The dApp prompts Alice with a request: âAllow Bob to spend 250 of your TokenName tokens until time T?â. Alice accepts in her Tron wallet. The wallet knows the tokenâs name and version, the contractâs address, and Tronâs chain ID, so it constructs the domain separator. It then creates the Permit message with
{owner: Alice, spender: Bob, value: 250, nonce: 0, deadline: T}. Suppose T is set to 10 minutes from now (a Unix timestamp). The wallet uses TIP-712 to show these details to Alice clearly, and she confirms. The wallet produces a signature(v, r, s)for Aliceâs private key on the hashed message. - Bob (or the dAppâs backend) calls
permit: Bobâs application receives the signature and now sends a transaction:token.permit(Alice, Bob, 250, T, v, r, s). Bob pays the energy/TRX for this transaction (Alice pays nothing). - Contract execution of
permit: The token contract checks:
- Current time <= T (if the transaction is within 10 minutes; assume yes).
nonces[Alice] == 0(it is 0, as expected).- Recovers signer from
(v,r,s)and the structured data hash. The recovered address is indeed Aliceâs address. Aliceis not the zero address.
All conditions pass. The contract setsallowance[Alice][Bob] = 250and emitsApproval(Alice, Bob, 250). It then setsnonces[Alice] = 1(increment by 1). The function returns success.
- Result: now Bob has an allowance of 250 tokens from Alice, and he can proceed to call
transferFrom(Alice, ... , up to 250)to use those tokens (for example, transferring them into a lending protocol or swapping them). This call by Bob will consume Bobâs resources as usual. From Aliceâs perspective, she authorized the spend with a signature and one blockchain transaction (which she didnât pay for). She can see on the blockchain an Approval event and that Bob has allowance 250. If Bob tries to reuse the same signature again, it will fail becausenonces[Alice]is now 1 (the contract would expect a nonce of 1 for a new permit, and the old signature had nonce 0). - Expiry scenario: if Bob never submitted the permit and the deadline T passed, the signature would become unusable (the contract would revert due to
block.timestamp > deadline). Alice could be assured that after time T, her signed approval evaporated. In this example Bob did use it in time, so itâs fine.
Implementation
Implementing this TIP in a TRC-20 token contract involves adding the three functions and the storage for nonces and domain separator. The logic can be adapted from existing Ethereum implementations of ERC-2612 (many of which are open-source and audited). For example, OpenZeppelinâs ERC20Permit library (Solidity) provides a blueprint for the permit function and related features, which could be ported to Tron with minimal changes (mainly adjusting for Tronâs chainId retrieval and ensuring address formatting).
On Tron, energy is used instead of gas; the complexity of permit (hashing and ecrecover) will consume some additional energy compared to a plain approve. This is typically on the order of a few tens of thousands of gas on Ethereum (for reference, ecrecover is 3000 gas for the precompile plus hashing costs). Tronâs energy model should handle this easily given Tronâs block gas limits and cheap energy via TRX staking. For the entity submitting the permit, the cost is not prohibitive (and they may monetize this via their service, e.g., a relayer could require watching an ad as in some hackathon projects).
Conclusion
The TRC-2612 Permit Standard represents a key evolution of Tronâs smart contract layer, bringing gasless approvals and off-chain authorization to the TRC-20 token model. By extending the existing standard with a permit() function built on TIP-712 structured signing, it enables seamless and cost-efficient token interactions without requiring users to spend TRX for separate approval transactions.
This enhancement improves usability and interoperability, aligning Tron with leading Layer-1 ecosystems like Ethereum while maintaining full backward compatibility. It empowers wallets and DeFi protocols to offer one-click operations, reducing friction in staking, swapping, and liquidity provisioning. Developers can now implement advanced UX patterns, such as meta-transactions and bundled interactions, with native support at the token level.
Beyond usability, TRC-2612 strengthens the security and consistency of approvals through nonce-based replay protection, explicit deadlines, and structured domain separation. These measures ensure every permit is verifiable, scoped, and tamper-proof.
By adopting TRC-2612, the Tron ecosystem reinforces its position as a developer-friendly and user-centric blockchain, setting the foundation for broader integrations, smoother DeFi experiences, and future innovations like account abstraction and cross-chain authorization standards. It is a decisive step toward making Tronâs token economy more accessible, secure, and interoperable across the Web3 landscape.