Modular abstraction comes from several proposals for the implementation of smart contract accounts with customizable functionality (see EIP-5005, EIP-6900). Modularity does not necessarily imply (or require) account abstraction. It can be applied to an arbitrary smart contract rather than necessarily a smart contract account), but rather adds customization by enabling or disabling modules - separate smart contracts that may have additional module dependencies.
Custom implementation of modular abstraction in PowerPool introduces plugins with different hook groups that can configure execution flow, job management, and keeper management.
Modularity will allow users (both keepers and job owners) to have better control over which functions they want to include in specific cases.
Developers can create several desirable outcomes without creating extra jobs, such as
automatically replenishing the job’s gas when the gas runs too low
disabling a failed keeper from future execution
automatically sending the earned reward when it reaches a certain amount.
Furthermore, it will allow users to create their libraries of these functions without relying on the PowerAgent team. This opens up the possibility of creating a module marketplace, like the Gnosis Safe apps ecosystem.
Now, if we have modularity and reliable on-chain automation in mind, could they work together to create a whole new standard of Web3 experience? The answer is yes! In the following section, we discuss the architectural choices and technical implementations of such a potential collaborative system.
The main benefit of modularity as a feature of the PowerAgent network is that it allows the user to natively specify different validation procedures (extending and duplicating the resolver). In addition, common validations that are easily parameterized (e.g., validating that a smart contract's balance (in a token) does not decrease after the automated transaction has taken place) can be implemented and audited; once done, these validations can serve as secure primitives (templates), drastically reducing the effort required to deploy new automated scenarios.
Technical details
Plugin interfaces are modified to allow selective enabling and disabling plugins for specific jobs, keepers, or agent-wide. Plugins are stored in corresponding registries and executed sequentially during hook execution. It enables superior composability as hooks can be assembled in several modules.
The functionality provided by the modular abstraction is divided into three categories:
HooksThese functions are called at arbitrary points of the execution flow of a function
Validation functionsThese functions are executed before and after the function call
Execution functionsThese functions provide desired (novel) functionality invoked by an open-ended execution call.For example, EIP-5005 contains specifications for functions like execTransactionFromModule
and execTransactionModuleReturnData
, which, as the name suggests, call an external contract (and optionally return results to be processed into the module).
PowerAgent core contract already has certain hooks implemented (for example, beforeExecution
, afterExecutionSucceeded
, afterExecutionReverted
, and others), which alter the behavior of the execution flow depending on the results of the transactions. However, it is desirable that the contract calls could change the hooks' behavior. In other words, the hooks are now modules, and it should be possible to configure the hook behavior by calling a particular function inside the contract.
There are two possible routes for implementing modularity inside the PowerAgent core contract: one requires full implementation of ERC6900, while the other only calls for moderate changes. Let us look at both options:
The route for implementation is then as follows:
Implement the following interfaces: IPluginManager
, IStandardExecutor
, IPluginExecutor
, IPluginLoupe
as described in the ERC-6900 documentation. Interaction with installation and uninstallation is onlyOwner.Here is the short explanation on each interface:
IPluginManager
, among others, implements the functions for modifying the plugin (module) registry - installing and deleting plugins.
IStandardExecutor
- an open-ended executor for an arbitrary smart contract call; it must disallow plugin calls.
IPluginExecutor
- a from-plugin executor which allows execution of functions from a plugin (including calls to external contracts).
IPluginLoupe
- the lens (i.e., a set of view functions that allows inspection of the module configuration of the contract; cf. diamond proxy loupe facet).
Hooks and validations should be invoked in Agent’s _beforeExecute
and _afterExecute
, execution functions - in the body of our execution function. All context of the parent functions will be passed into the child function, and all other queries are done from within the child functions using getters.
Plugins enter a registry on installation and are zeroed out on uninstallation, and invocation is done by iterating over this registry.
To have the Job-specific module configuration, modifications to the interface are made. All module management functions (installation and uninstallation) accept a list of job keys; if a caller does not own all these jobs, the call is reverted. Each job’s plugins enter a registry, and these registries form a registry themselves, indexed by job keys. Invocation is then done like in 2. and 3., but for the registry of the current job.
In simpler words, instead of enabling modules for the whole Agent contract, there is a registry indexed by the job keys which contains the modules enabled for each job key: a naive representation is mapping(jobKey => mapping(uint256 => IPlugin))
. In this paradigm, agent-wide module management is still possible; agent-wide module registry is assigned a special job key of zero that is normally impossible to obtain.
Another approach would be to implement an ERC-6900 - inspired architecture for modularity; in this case, the roadmap would look as follows:
The plugins are as in ERC-6900. All hooks are now related to one of the already existing hook groups:
Execution flow hooks: beforeExecute
, afterExecute
, afterExecutionSucceeded
, afterExecutionReverted
Job management hooks: afterRegisterJob
, afterDepositJobCredits
, afterWithdrawJobCredits
, afterAcceptJobTransfer
Keeper management hooks: beforeInitiateRedeem
, afterInitiateRedeem
, afterRegisterAsKeeper
Plugin interfaces IPluginManager
, IStandardExecutor
, IPluginExecutor
, IPluginLoupe
are modified so that they accept two additional arguments: jobKey
and keeperId
for each plugin manipulation function (installation and uninstallation). This modification allows selective enabling and disabling of the plugins for either each job, or each keeper, or agent-wide. For example, to enable a plugin for a specific job key, the plugin management function should be called passing a jobKey
as an additional argument. If the caller is not an owner of the job, the function will revert. Otherwise, the plugin will be enabled for this job as specified in the ERC6900 implementation. The same is true for keeper-specific plugin installation/uninstallation. To install/uninstall a plugin agent-wide, the caller must be the owner of the PowerAgent contract, and other arguments must be zero.
All plugins (job specific, keeper specific and agent-wide) are stored in the corresponsing registries. All agent-wide registries are thus automatically zero-indexed in the corresponding registry.
During a hook execution, an iteration over the registries of the plugins belonging to the Job and to the current keeper is performed. Each installed plugin’s content is invoked sequentially, passing the parent hook context into it.
All current hooks can be assembled in several modules (as shown in item 1).
Modular abstraction allows for customization of smart contract functionality by enabling or disabling modules. It improves user control, enabling desirable outcomes without creating extra jobs. Users can create their function libraries, potentially leading to a marketplace for modules.
In the PowerAgent network, modular abstraction complements on-chain automation by allowing users to specify different procedures. This reduces the effort required to deploy new automated scenarios.
Modularity can be implemented in PowerAgent through ERC6900 or a custom ERC6900-inspired architecture. ERC6900 modifies the core contract to support modules, while the custom architecture introduces plugins with different hook groups and selective enabling/disabling for jobs, keepers, or agent-wide.
PowerPool’s PowerAgent network is a decentralized, permissionless network of Keepers bots. It allows users to register an on-chain scenarios that require execution at a specified time interval or when specified conditions are met. Once such a scenario (Job) is registered in the core PowerAgent contract, an event is emitted, which is caught by all keepers online. The Agent contact randomly assigns one of the keepers for the subsequent job execution. If a keeper fails to execute the Job properly, its stake will be slashed and another keeper will do its job.
Such mechanism design allows for robust, secure, and cheap automation for arbitrary on-chain scenarios, an example of which would be regular rewards claiming, DEX limit order, liquidation protection, and many others.
PowerAgent network can be easily integrated to AA/Intents wallets and apps, custom UIs, and third party protocols.