ERC-7405: Modular Wallet Interface
ERC-7405 builds on ERC-7579 (modular smart account) to define a standardised plugin lifecycle for wallets. Wallet vendors expose a common module-installation / removal / configuration API so dApps can ship plugins (yield-routing, bridge integration, MEV protection, custom validators) that any ERC-7405-compliant wallet can install.
Goals:
- Wallet portability for plugins — write once, deploy across Safe, Argent, ZeroDev, Coinbase Smart Wallet, etc.
- Granular permissions per module — modules declare what they need; user approves the scope.
- Composability — multiple modules per wallet, with explicit conflict resolution.
Required Interface (delta from ERC-7579)
interface IERC7405 {
event ModuleInstalled(uint256 indexed moduleType, address indexed module, bytes initData);
event ModuleUninstalled(uint256 indexed moduleType, address indexed module);
function installModule(uint256 moduleType, address module, bytes calldata initData) external;
function uninstallModule(uint256 moduleType, address module, bytes calldata deInitData) external;
function isModuleInstalled(uint256 moduleType, address module, bytes calldata additionalContext)
external view returns (bool);
}Module types include validators (1), executors (2), fallback handlers (3), and hooks (4) — same shape as ERC-7579, with additional helpers for context-aware queries.
Neo Equivalent: NEP-30 Verify + Module Dispatch
The Neo port composes with the ERC-7579 mirror: the smart account's Verify (NEP-30) iterates installed validator modules; method calls dispatch to executor modules; fallback hooks fire on unknown selectors.
public static void InstallModule(BigInteger moduleType, UInt160 module, ByteString initData)
{
if (!Runtime.CheckWitness(GetOwner())) throw new Exception("AA:NotOwner");
var key = ModuleKey(moduleType, module);
Storage.Put(Storage.CurrentContext, key, initData);
Contract.Call(module, "onInstall", CallFlags.All, new object[] { initData });
OnModuleInstalled(moduleType, module, initData);
}
public static void UninstallModule(BigInteger moduleType, UInt160 module, ByteString deInitData)
{
if (!Runtime.CheckWitness(GetOwner())) throw new Exception("AA:NotOwner");
var key = ModuleKey(moduleType, module);
Storage.Delete(Storage.CurrentContext, key);
try
{
Contract.Call(module, "onUninstall", CallFlags.All, new object[] { deInitData });
}
catch { /* swallow; uninstall must succeed even if hook reverts */ }
OnModuleUninstalled(moduleType, module);
}
public static bool IsModuleInstalled(BigInteger moduleType, UInt160 module)
=> Storage.Get(Storage.CurrentContext, ModuleKey(moduleType, module)) is not null;| ERC-7405 (Ethereum) | Neo Equivalent | Notes |
|---|---|---|
installModule(...) | InstallModule(...) owner-witness-checked + onInstall hook | Same lifecycle |
uninstallModule(...) | UninstallModule(...) with try-swallow on uninstall hook | Forceful cleanup |
isModuleInstalled(...) | IsModuleInstalled(...) view | Direct port |
| Module types (validator/executor/fallback/hook) | Same enum encoding | Wire-compatible |
| Per-module init data blob | Stored verbatim in module-key storage | Module reads its own init data |
Composition
- ERC-7579 — modular smart account base. ERC-7405 standardises the install/uninstall lifecycle.
- ERC-4337 — account abstraction. Modules are the validator / paymaster / signature plugins of the AA stack.
- ERC-7677 — paymaster. Paymasters are one type of module ERC-7405 manages.
- ERC-7715 — permission grants. Grant modules enforce the per-(grant) authorisation logic.
Migration Notes
For Solidity smart-wallet plugin authors:
- The plugin contract implements
onInstall(initData)andonUninstall(deInitData)callbacks for lifecycle handling. - Wallets call these on install/uninstall — plugin reverts cause install to fail (good) but uninstall always proceeds (intentional; prevents stuck modules).
- Module discovery happens via the wallet's
IsModuleInstalledview; off-chain registries enumerate installed modules per wallet.
For Neo wallet developers, implementing ERC-7405 means exposing the install/uninstall surface; modules become normal Neo contracts loaded by the wallet's Verify and Execute methods.
