tip-7579.md
July 23, 2026 ยท View on GitHub
tip: 7579
title: Minimal Modular Smart Accounts
description: Minimal account and module interfaces for interoperable smart accounts
author: yanghang8612@gmail.com
discussions-to: https://github.com/tronprotocol/tips/issues/880
status: Draft
type: Standards Track
category: TRC
created: 2026-05-26
requires: 165, 1271, 2771
Simple Summary
Define one portable interface for installing and using validators, executors, hooks, and fallback handlers in TRON smart accounts.
Abstract
This standard adapts ERC-7579 to define minimal interfaces and behavior for modular smart accounts on TRON. It standardizes execution, module installation and removal, module types, lifecycle observability, signature forwarding, and capability discovery while leaving account architecture and activation mechanisms open.
The interface is independent of TRC-4337 and EIP-7702. A plain TVM contract, a TRC-4337 account, or a future protocol-activated account can expose the same module surface.
Motivation
Current smart accounts use wallet-specific validator, executor, hook, and fallback-handler interfaces. Users cannot change capabilities without migrating accounts, and module authors must integrate separately with every account vendor. A small common surface enables upgrade-in-place, portable modules, and a shared foundation for permission delegation and account abstraction.
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.
Definitions and Module Types
- A smart account is a TVM contract account with modular functionality.
- A module is a contract that provides self-contained account functionality.
- Validation determines whether an operation may execute.
- Execution invokes one or more operations from the account.
Module type identifiers are:
| Type | Identifier |
|---|---|
| Validator | 1 |
| Executor | 2 |
| Fallback handler | 3 |
| Hook | 4 |
A module MAY implement more than one type.
Single Authorization Model
A compliant TRON account MUST commit to one authorization model at deployment or initialization. It MAY be backed by TRON native permissions (TIP-16/TIP-105) or by TRC-7579 validator modules, but it MUST NOT route the same operation through both models.
Module installation, removal, safe-mode changes, and account-management operations MUST be authorized exclusively by that chosen model. A native-permission-backed account MAY expose discovery functions while reporting that validator modules are unsupported.
Authorization MUST preserve the initiating context across account self-calls. A call originating from executeFromExecutor MUST NOT become authorized for installation, removal, safe-mode, or other account-management functions merely because a nested call has msg.sender == address(this). An account MUST either reject those management selectors in executor-originated self-calls or carry enough context to re-apply the chosen authorization model.
Execution Interface
interface ITRC7579Execution {
function execute(bytes32 mode, bytes calldata executionCalldata) external;
function executeFromExecutor(bytes32 mode, bytes calldata executionCalldata)
external returns (bytes[] memory returnData);
}
execute MUST enforce the account's authorization policy. executeFromExecutor MUST be callable only by a properly installed executor. Unsupported modes MUST revert.
The bytes32 mode layout is:
| Call type | Exec type | Reserved | Mode selector | Mode payload |
|---|---|---|---|---|
| 1 byte | 1 byte | 4 bytes | 4 bytes | 22 bytes |
Call types are 0x00 single call, 0x01 batch call, 0xfe static call, and 0xff delegate call. Exec type 0x00 reverts on subcall failure; 0x01 handles the error without necessarily reverting. Accounts are not required to support every mode.
Execution calldata MUST use the upstream encoding:
- single call:
abi.encodePacked(target, value, callData); - delegate call:
abi.encodePacked(target, callData); and - batch call:
abi.encode(Execution[]), whereExecutionis(address target, uint256 value, bytes callData).
The value is TRX denominated in sun. TRC-20 transfers are ordinary calldata with value zero. TRC-10 MUST NOT extend or reinterpret this encoding; implementations that need TRC-10 execution SHOULD use a dedicated executor or wrapper contract.
Accounts MAY implement TRC-4337 v0.7 executeUserOp(PackedUserOperation,bytes32). If implemented, it MUST be restricted to the canonical EntryPoint and SHOULD execute userOp.callData[4:] by delegatecall so the original EntryPoint caller context is preserved.
Account Configuration
interface ITRC7579AccountConfig {
function accountId() external view returns (string memory);
function supportsExecutionMode(bytes32 encodedMode) external view returns (bool);
function supportsModule(uint256 moduleTypeId) external view returns (bool);
}
accountId() MUST be non-empty and SHOULD follow vendor.account.semver. Capability queries MUST return false, rather than revert, for unsupported functionality.
Module Configuration and Observability
interface ITRC7579ModuleConfig {
event ModuleInstalled(uint256 moduleTypeId, address module);
event ModuleUninstalled(uint256 moduleTypeId, address module);
function installModule(
uint256 moduleTypeId, address module, bytes calldata initData
) external;
function uninstallModule(
uint256 moduleTypeId, address module, bytes calldata deInitData
) external;
function isModuleInstalled(
uint256 moduleTypeId, address module, bytes calldata additionalContext
) external view returns (bool);
}
Installation MUST:
- be authorized by the single chosen authorization model;
- reject an already-installed
(type, module)pair; - enter a non-usable
installingstate and reject reentrant lifecycle changes for the same pair; - call
onInstall(initData)and revert the entire transition if it fails; - mark the pair installed and usable only after the callback returns; and
- emit
ModuleInstalledonly after successful initialization.
Removal MUST apply the symmetric authorization and reentrancy requirements. Before calling onUninstall, the account MUST enter an uninstalling state that disables all type-specific authority of the pair. A callback failure MUST revert the entire removal and restore the prior state; after success the account MUST clear installation and extension state and then emit ModuleUninstalled. Accounts MUST distinguish installed modules by type when authorizing type-specific calls.
The two lifecycle events are the canonical install/remove history. Accounts SHOULD expose enumeration or an indexable view in addition to isModuleInstalled.
Modules MAY implement the following metadata extension and, if so, MUST advertise it through TRC-165:
interface ITRC7579ModuleMetadata {
function moduleMetadata() external view returns (
string memory name,
string memory version,
uint256[] memory moduleTypes,
bool usesDelegateCall,
bool canExecuteAutomatically,
string memory uninstallInfoURI
);
}
Metadata is informational and MUST NOT grant authority or replace isModuleType. Its values SHOULD be stable for a deployed module version.
Core Module Interface
interface ITRC7579Module {
function onInstall(bytes calldata data) external;
function onUninstall(bytes calldata data) external;
function isModuleType(uint256 moduleTypeId) external view returns (bool);
}
Lifecycle functions MUST revert on failure. A multi-type module MAY include moduleTypeId in its lifecycle data.
Validators and Signatures
Validator modules MUST implement the core module interface and the activation-independent signature interface:
interface ITRC7579Validator is ITRC7579Module {
function isValidSignatureWithSender(
address sender, bytes32 hash, bytes calldata signature
) external view returns (bytes4);
}
interface ITRC7579Validator4337 is ITRC7579Validator {
function validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash
) external returns (uint256 validationData);
}
ITRC7579Validator4337 is an optional TRC-4337 adapter and imports its PackedUserOperation; it is not part of the core validator requirement.
Under this activation-independent TRON profile, module type 1 identifies the core signature-validator capability only. A module implementing ITRC7579Validator4337 MUST advertise that extension through TRC-165. A TRC-4337 account MUST check both isModuleType(1) and the ITRC7579Validator4337 interface id before accepting the module for UserOperation validation; registries and SDKs MUST NOT infer 4337 support from module type 1 alone.
Accounts MUST implement TRC-1271. When forwarding signature validation, the account MUST pass the original msg.sender of the TRC-1271 call as sender, MUST sanitize any validator-selection encoding from signature, and SHOULD return the validator's bytes4 result unchanged. The validator MUST return the TRC-1271 magic value only for a valid signature and MUST return a non-magic value for signature mismatch; unexpected errors MAY revert. TRC-712 hashes use ABI-level 20-byte addresses and chainId = block.chainid & 0xffffffff; a wallet MUST NOT add a personal-message prefix to a TRC-712 digest.
Hooks
Hooks are optional and use the following ABI:
interface ITRC7579Hook is ITRC7579Module {
function preCheck(
address msgSender,
uint256 value,
bytes calldata msgData
) external returns (bytes memory hookData);
function postCheck(bytes calldata hookData) external;
}
If supported, an account MUST call each applicable preCheck before execution and pass its returned hookData to the corresponding postCheck afterward. The account MUST define deterministic hook order.
A security-relevant validator or hook whose sub-execution fails, including from insufficient energy, SHOULD fail the whole operation rather than be swallowed by a non-reverting execution mode. The account SHOULD reserve sufficient energy for postCheck, or treat a non-completing postCheck as operation failure.
Fallback Handlers
Fallback handlers are optional. If installed, the account MUST route by selector and invoke the handler using call or staticcall. The original sender MUST be appended following TRC-2771. A fallback handler that performs authorization MUST NOT trust msg.sender, which is the account; it MUST recover the appended original caller using the TRC-2771 _msgSender() rule. Core account management SHOULD NOT be implemented only through fallback routing.
Optional Safe Mode Extension
Safe mode is optional, but an implementation offering it MUST expose a predictable interface:
interface ITRC7579SafeMode {
event ModulePaused(uint256 indexed moduleTypeId, address indexed module);
event ModuleUnpaused(uint256 indexed moduleTypeId, address indexed module);
function setModulePaused(uint256 moduleTypeId, address module, bool paused) external;
function isModulePaused(uint256 moduleTypeId, address module) external view returns (bool);
}
Safe-mode changes MUST use the account's single authorization model. Support MUST be advertised through TRC-165. setModulePaused MUST revert unless the pair is currently installed and not in a lifecycle transition. A newly installed or reinstalled pair starts unpaused; successful uninstall MUST clear its pause state. isModulePaused MUST return false for an uninstalled pair.
Pausing is fail-closed and type-specific:
- selecting a paused validator MUST fail validation; the account MUST NOT silently skip it;
- an operation to which a paused hook applies MUST fail before execution;
executeFromExecutorMUST reject a paused executor; and- routing to a paused fallback handler MUST fail.
Pausing does not uninstall a module: isModuleInstalled MUST continue to return true, while isModulePaused reports the independent pause state. An account offering safe mode MUST preserve at least one recovery and account-management path that installed modules cannot pause. Pause and unpause events MUST be emitted only after the state transition succeeds.
Wallet Installation Display
Before authorizing installation, wallets SHOULD show the module address and type, version, requested capabilities, delegate-call use, hook order and blocking behavior, fallback scope, automatic-execution ability, uninstall path, and TRC-1271/TRC-712 assumptions.
Rationale
The interface remains independent of how the smart account is activated. Mutual exclusivity applies only to authorization; it avoids two parallel validation paths while preserving one discovery and execution surface for wallets.
The upstream execution-mode encoding is retained byte-for-byte so module code and SDKs can port without a TRON-only codec. TRC-10 support is moved to wrappers because extending every batch tuple with token id and token value would destroy that portability.
Lifecycle events and optional standard safe mode address wallet security observability without forcing storage-heavy enumeration or pause machinery on minimal accounts.
Backwards Compatibility
This standard does not modify native accounts, TIP-16/TIP-105 permissions, or existing smart accounts. Existing contracts can comply through an upgrade or adapter only if their authorization architecture can enforce the single-model and lifecycle requirements.
Security Considerations
- Installing a module grants code authority over an account and can be equivalent to transferring control. Install and remove paths require the same security as asset transfers.
- Delegate-call modules execute in account storage context and SHOULD receive enhanced review and explicit wallet warnings.
- Hooks can deny service by reverting; conversely, swallowed or energy-starved hooks can bypass policy. Accounts SHOULD fail closed for security hooks.
- Fallback handlers expand the callable surface and MUST be selector-scoped.
- Validator selection embedded in signatures or UserOperations MUST be sanitized before forwarding.
- Uninstall MUST remove every authority and callback registered during install. A module claiming to be removed while retaining executor or hook access is non-compliant.
onUninstallis atomic with removal and may revert, allowing a malicious module to block removal. Safe mode can disable such a module when implemented, but does not make it uninstalled.- Executor-originated self-calls MUST NOT bypass enhanced authorization on account configuration functions.
- Native-permission and module-validator paths MUST NOT be stacked for the same operation.
Copyright
Copyright and related rights waived via CC0.