Skip to content

Latest commit

 

History

History
366 lines (263 loc) · 20.6 KB

File metadata and controls

366 lines (263 loc) · 20.6 KB
tip: 4337
title: Account Abstraction Using Alt Mempool
description: Smart-contract accounts without consensus-layer changes
author: yanghang8612@gmail.com
discussions-to: https://github.com/tronprotocol/tips/issues/881
status: Draft
type: Standards Track
category: TRC
created: 2026-05-26
requires: 165, 712, 1271

Simple Summary

Enable programmable smart accounts, sponsored transactions, batching, and recovery through a shared EntryPoint and bundler network.

Abstract

This standard adapts ERC-4337 to introduce account abstraction on TRON without changing consensus. Users create higher-level UserOperation objects for smart contract accounts. Bundlers collect them through an alternative mempool or private service, package them into a normal TriggerSmartContract transaction, and call a singleton EntryPoint contract. Accounts provide programmable validation; paymasters can sponsor costs; factories enable counterfactual deployment.

V1 targets the ERC-4337 v0.7 PackedUserOperation ABI and adapts fee semantics to TRON's energy and bandwidth resource model.

Motivation

TRON native permissions provide weighted multisignature and operation-scoped keys, but not arbitrary contract validation, counterfactual deployment, standardized paymasters, batched smart-account execution, session keys, or non-custodial recovery. These capabilities improve onboarding, especially the first action from an account with no TRX, and provide a common account layer for wallets and automated agents.

Specification

The key words "MUST", "MUST NOT", "REQUIRED", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" are to be interpreted as described in RFC 2119 and RFC 8174.

Architecture

  • A UserOperation describes an account action and its validation/resource envelope.
  • A smart account validates and executes UserOperations.
  • The EntryPoint validates, accounts for resources, deploys accounts through factories, executes operations, and settles deposits.
  • A bundler submits one or more UserOperations in an ordinary TRON transaction.
  • A paymaster optionally validates and sponsors an operation.
  • An aggregator optionally validates aggregated signatures.

No protocol or Super Representative change is required. The alternative mempool may initially be an off-chain service operated by a wallet, exchange, or payment provider.

Version Target

V1 is pinned to the ERC-4337 v0.7 implementation at commit 7af70c8993a6f42973f520ae0752386a5032abe7. It uses the v0.7 packed on-chain ABI and MUST NOT use the deprecated v0.6 unpacked UserOperation. EIP-7702 authorization tuples and later EntryPoint ABI additions are outside V1; a future TIP revision may add them after the TRON EIP-7702 track stabilizes.

Packed UserOperation

struct PackedUserOperation {
    address sender;
    uint256 nonce;
    bytes initCode;
    bytes callData;
    bytes32 accountGasLimits;
    uint256 preVerificationGas;
    bytes32 gasFees;
    bytes paymasterAndData;
    bytes signature;
}

Packed fields follow v0.7:

  • accountGasLimits = uint128(verificationGasLimit) || uint128(callGasLimit);
  • gasFees = uint128(maxPriorityFeePerGas) || uint128(maxFeePerGas); and
  • non-empty paymasterAndData = paymaster(20) || paymasterVerificationGasLimit(16) || paymasterPostOpGasLimit(16) || paymasterData.

TRON addresses in packed byte fields are the 20-byte ABI body with the 0x41 prefix removed. nonce uses a 192-bit key and 64-bit sequence so accounts may maintain independent ordered lanes.

EntryPoint

interface ITRC4337StakeManager {
    struct DepositInfo {
        uint256 deposit;
        bool staked;
        uint112 stake;
        uint32 unstakeDelaySec;
        uint48 withdrawTime;
    }

    function getDepositInfo(address account)
        external view returns (DepositInfo memory info);
    function balanceOf(address account) external view returns (uint256);
    function depositTo(address account) external payable;
    function addStake(uint32 unstakeDelaySec) external payable;
    function unlockStake() external;
    function withdrawStake(address payable withdrawAddress) external;
    function withdrawTo(address payable withdrawAddress, uint256 amount) external;
}

interface ITRC4337EntryPoint is ITRC4337StakeManager {
    error SenderAddressResult(address sender);
    error DelegateAndRevert(bool success, bytes ret);

    struct UserOpsPerAggregator {
        PackedUserOperation[] userOps;
        ITRC4337Aggregator aggregator;
        bytes signature;
    }

    function handleOps(
        PackedUserOperation[] calldata ops,
        address payable beneficiary
    ) external;

    function handleAggregatedOps(
        UserOpsPerAggregator[] calldata opsPerAggregator,
        address payable beneficiary
    ) external;

    function getUserOpHash(PackedUserOperation calldata userOp)
        external view returns (bytes32);

    function getNonce(address sender, uint192 key) external view returns (uint256);
    function incrementNonce(uint192 key) external;

    function getSenderAddress(bytes memory initCode) external;
    function delegateAndRevert(address target, bytes calldata data) external;
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

interface ITRC4337Aggregator {
    function validateSignatures(
        PackedUserOperation[] calldata userOps,
        bytes calldata signature
    ) external view;

    function validateUserOpSignature(PackedUserOperation calldata userOp)
        external view returns (bytes memory sigForUserOp);

    function aggregateSignatures(PackedUserOperation[] calldata userOps)
        external view returns (bytes memory aggregatedSignature);
}

An account returning a non-zero aggregator in validationData MUST be submitted through handleAggregatedOps; plain handleOps MUST reject it. The EntryPoint MUST use getNonce(sender, key) and update only the low 64-bit sequence of the selected 192-bit nonce key. getSenderAddress and delegateAndRevert always return their results by reverting with the v0.7 SenderAddressResult and DelegateAndRevert custom errors. EntryPoint MUST advertise its EntryPoint, stake-manager, and nonce-manager capabilities through TRC-165.

There MUST be one canonical, audited V1 EntryPoint deployment per TRON network. Its address, bytecode hash, source commit, deployment transaction, and supported interface version MUST be recorded in this document before any deployment claims canonical status. While this draft contains no such deployment table, implementations MUST treat EntryPoints as explicitly configured and MUST display the address; they MUST NOT infer a canonical address from Ethereum.

TVM CREATE2 produces a TRON address, so Ethereum's canonical EntryPoint address is not portable.

Smart Account Interface

interface ITRC4337Account {
    function validateUserOp(
        PackedUserOperation calldata userOp,
        bytes32 userOpHash,
        uint256 missingAccountFunds
    ) external returns (uint256 validationData);
}

The account MUST:

  1. accept calls only from its configured canonical EntryPoint;
  2. validate the signature or other authorization over userOpHash;
  3. return signature-failure value 1, rather than revert, for signature mismatch;
  4. transfer at least missingAccountFunds to EntryPoint when the account pays; and
  5. return packed validity bounds/aggregator data compatible with v0.7.

Any error other than signature mismatch MUST revert. Accounts MAY implement executeUserOp(PackedUserOperation,bytes32) and SHOULD expose modular execution through TRC-7579.

UserOperation Hash and Replay Protection

V1 retains the v0.7 legacy hash construction and changes only the network value to the TRC-712 network id. It is not the newer ERC-4337 EIP-712 PackedUserOperation digest.

bytes32 inner = keccak256(abi.encode(
    userOp.sender,
    userOp.nonce,
    keccak256(userOp.initCode),
    keccak256(userOp.callData),
    userOp.accountGasLimits,
    userOp.preVerificationGas,
    userOp.gasFees,
    keccak256(userOp.paymasterAndData)
));

bytes32 userOpHash = keccak256(abi.encode(
    inner,
    address(entryPoint),
    uint256(block.chainid & 0xffffffff)
));

The signature field is excluded. Dynamic byte fields are represented by their keccak256 hash exactly as shown. Addresses MUST use ABI-level 20-byte encoding. Wallets and contracts MUST hash the same truncated network value and MUST NOT add a personal-message prefix unless the account's own validator explicitly defines an additional wrapping scheme. Binding the EntryPoint prevents replay through a different EntryPoint version on the same network.

Validation and Execution

handleOps performs a verification phase and an execution phase. For each operation it MUST:

  1. deploy sender through initCode when necessary and verify the resulting address;
  2. validate and update the keyed nonce;
  3. compute the required prefund from signed limits and fees;
  4. call account validation and, when present, paymaster validation;
  5. enforce validity bounds and validation-resource limits;
  6. execute only the validated callData; and
  7. charge the account/paymaster deposit and compensate beneficiary within the signed envelope.

Validation failures MUST revert using the v0.7 custom errors FailedOp, FailedOpWithRevert, or SignatureValidationFailed, as applicable. A target-call failure after successful validation MUST NOT by itself revert the whole bundle: EntryPoint MUST emit UserOperationEvent with success == false and MAY emit UserOperationRevertReason. Accounts and paymasters MUST restrict validation/post-operation entry points to EntryPoint.

Energy and Fee Field Semantics

The packed ABI is unchanged but values have a TRON profile:

Field TRON meaning
callGasLimit maximum Energy forwarded for account execution
verificationGasLimit maximum Energy for account creation/validation
paymaster limits maximum Energy for paymaster validation and post-operation
preVerificationGas signed Energy-equivalent flat amount for outer execution overhead and nominal bandwidth
maxFeePerGas maximum sun per Energy the signer accepts
maxPriorityFeePerGas MUST equal maxFeePerGas; no separate priority auction

EntryPoint MUST reject unequal fee fields. It MUST reject when the effective sun-per-Energy settlement rate exceeds the signed value.

Both fee fields MUST be non-zero. Because they are equal, settlementRate = maxFeePerGas. For an operation without a paymaster:

requiredEnergy = verificationGasLimit + callGasLimit + preVerificationGas
requiredPrefund = requiredEnergy * settlementRate

For a paymaster operation, requiredEnergy additionally includes paymasterVerificationGasLimit + paymasterPostOpGasLimit. EntryPoint MUST reject an insufficient account or paymaster deposit before execution.

The charged Energy-equivalent is the measured EntryPoint validation, execution, and post-operation Energy plus the signed flat preVerificationGas. As in the pinned v0.7 implementation, it MUST also include a 10% penalty on unused callGasLimit + paymasterPostOpGasLimit capacity when that combined limit exceeds measured execution and post-operation Energy:

unusedExecutionEnergy = callGasLimit + paymasterPostOpGasLimit
    - measuredExecutionAndPostOpEnergy
unusedEnergyPenalty = floor(unusedExecutionEnergy * 10 / 100)

The penalty is zero when the measured amount reaches the combined limit. It is included in UserOperationEvent.actualGasUsed under the v0.7 ABI's field name and in actualCost = chargedEnergy * settlementRate. Actual cost MUST NOT exceed the prefund reserved for the operation. EntryPoint MUST return unused prefund to the payer's deposit and transfer the aggregate actual cost of the bundle to beneficiary.

TRON bandwidth is charged before VM execution and is not observable by EntryPoint. It is represented by the signed flat preVerificationGas and controlled by bundler admission policy, not re-measured on-chain.

Pre-Verification Resource Estimate

preVerificationGas is signed, so it MUST be estimated before the account creates its signature. A compliant estimator MUST depend only on the unsigned UserOperation fields available at that time, a declared expected signature length, and the bundler's published price and overhead assumptions. It MUST NOT depend on the operation's eventual bundle index or bundle size, the beneficiary, reference block, timestamp, expiration, permission id, or the bundler's final outer signatures.

Before signing, the bundler MUST return its estimate through eth_estimateUserOperationGas and make the assumptions used for that estimate available to the wallet. At minimum, the estimate MUST cover the operation's allocated EntryPoint overhead and a conservative bandwidth estimate converted to Energy-equivalent units:

preVerificationGas >= outerEnergyOverhead
    + ceil(estimatedBandwidthBytes * bandwidthUnitPriceSnapshot / settlementRate)

This V1 deliberately does not prescribe a universal exact byte-allocation algorithm: the final outer transaction does not exist when the UserOperation is signed. For final admission and risk control, a bundler SHOULD recompute the actual outer transaction bandwidth using the official TRON estimator:

B(tx) = protobufSerializedSize(tx with ret cleared) + 64

Bundlers MUST publish their expected-signature-length rule, resource-price snapshot, and overhead schedule. Free daily bandwidth, staked or delegated bandwidth, final bundle composition, and price changes can make actual cost diverge from the signed flat estimate. The bundler bears that basis risk and MUST NOT charge the payer above the signed prefund envelope.

Paymasters and Deposits

Paymasters and EntryPoint deposits use the v0.7 interfaces:

interface ITRC4337Paymaster {
    enum PostOpMode { opSucceeded, opReverted, postOpReverted }

    function validatePaymasterUserOp(
        PackedUserOperation calldata userOp,
        bytes32 userOpHash,
        uint256 maxCost
    ) external returns (bytes memory context, uint256 validationData);

    function postOp(
        PostOpMode mode,
        bytes calldata context,
        uint256 actualEnergyCost,
        uint256 actualUserOpFeePerEnergy
    ) external;
}

A paymaster deposit is liquid TRX held by EntryPoint for immediate settlement. EntryPoint stake is a separate liquid-TRX anti-DoS lock with a withdrawal delay. Neither is staked Energy, and neither MUST be represented as Stake 2.0 resource delegation. Bundlers MUST state which entities must hold EntryPoint stake under their admission policy.

TRON's consume_user_resource_percent may cover simple contract Energy sponsorship, but it is not a substitute for conditional paymaster policy, token-denominated fees, account deployment, or cross-application interoperability.

Paymasters and bundlers SHOULD expose stable rejection categories for invalid signature, validity window, insufficient deposit, policy rejection, unsupported aggregator, resource-limit underestimation, and simulation failure.

Bundler Operating Model and RPC

V1 MAY operate with private or permissioned bundlers; a public alt mempool is not required for conformance. A wallet MUST disclose:

  • the bundler and paymaster service used;
  • relevant operator/trust or failure-responsibility information;
  • quoted resource prices and expiry; and
  • whether the UserOperation is merely queued off-chain, accepted by a bundler, submitted as an outer transaction, or confirmed on-chain.

An off-chain queue identifier MUST NOT be displayed as a transaction id.

Bundlers SHOULD implement the ERC-4337 v0.7 / ERC-7769 JSON-RPC method shapes (eth_sendUserOperation, eth_estimateUserOperationGas, eth_getUserOperationByHash, and eth_getUserOperationReceipt) with TRON network endpoints. The off-chain request uses the unpacked v0.7 wire fields, including factory/factoryData, individual gas limits and fees, and individual paymaster fields. The bundler packs them into PackedUserOperation only for the on-chain EntryPoint call. Supporting a shared RPC is RECOMMENDED, not required for a closed initial deployment.

Validation Rules

Bundlers MUST simulate validation before admission and apply a documented anti-DoS policy derived from ERC-7562. The policy MUST constrain validation Energy, storage access, external calls, code deployment, paymaster behavior, unstaked entities, and reputation. TRON limits and stake definitions MUST be published by the bundler; Ethereum values MUST NOT be copied without measurement.

EntryPoint's on-chain validity does not depend on a particular mempool policy. Different compliant bundlers may reject an operation that EntryPoint would accept, but MUST return an actionable reason.

tx.origin

During smart-account execution, tx.origin is the bundler and msg.sender at the target is the smart account. Contracts MUST NOT identify the user by tx.origin. Integrations SHOULD use msg.sender and TRC-1271 for account signatures.

Rationale

Why v0.7

The packed v0.7 ABI has broad production tooling and avoids starting with deprecated v0.6. It also avoids blocking TRC-4337 on an unfinished EIP-7702 adaptation. A later version can upgrade the canonical EntryPoint through a separate, explicit compatibility revision.

Why Keep Both Fee Fields

Bundler SDKs and account contracts assume v0.7 packing. Keeping both fields preserves ABI compatibility; requiring equality removes an otherwise meaningless priority dimension on TRON.

Native Permissions and Simpler Standards

Native permissions remain preferable for weighted keys and protocol-operation restrictions. TRC-3009 is simpler for direct gasless token transfers. TRC-4337 earns its overhead for programmable validation, recovery, batching, account deployment, and conditional sponsorship.

Practical Flow Comparison

Flow Preferred starting point Where TRC-4337 adds value
Native TRX transfer ordinary/native-permission transaction programmable recovery or sponsorship
TRC-20 payment ordinary transfer or TRC-3009 batching, account policy, paymaster
Multi-call DApp action ordinary calls one authorization and atomic account batch
First action without TRX application sponsor workaround factory + paymaster onboarding
Account recovery native key/permission management arbitrary social/passkey recovery logic

Actual cost depends on contract bytecode, calldata, current resource prices, and staked resources. Implementers SHOULD publish measured comparisons for these flows rather than universal estimates.

Backwards Compatibility

No consensus rule changes. Existing native and smart accounts continue to work. Pre-TRC-4337 smart accounts require an upgrade or wrapper that implements validateUserOp; ordinary target contracts may need changes only if they incorrectly depend on tx.origin.

Security Considerations

  • EntryPoint concentrates ecosystem risk and requires independent audits, formal verification, reproducible deployment, and bytecode-hash pinning.
  • Account validation MUST bind the operation to sender, EntryPoint, and truncated TRON network id.
  • Bundlers can censor, delay, or drop queued operations. Submission transparency is mandatory, and wallets SHOULD support rebroadcast or alternate bundlers where safe.
  • Paymasters can deny service and must not be able to mutate the user's signed execution.
  • Simulation and validation rules are necessary to prevent alt-mempool resource exhaustion.
  • Deposits are liquid settlement balances and are exposed to EntryPoint bugs; they are not protected by Stake 2.0 locks.
  • The nominal bandwidth formula does not reveal actual free/staked bandwidth consumption. Bundlers bear the difference.
  • Factories MUST ensure initCode deploys the expected sender; counterfactual-address mismatch MUST fail.
  • Smart accounts and target contracts MUST avoid tx.origin authorization.

Copyright

Copyright and related rights waived via CC0.