ModuleKit
Tools
Building modules
Interfaces
ValidatorBase
: Interface for Validators to inherit from.ExecutorBase
: Interface for Executors to inherit from.
Templates
Conditions
To allow for high flexibility, ModuleKit allows users to configure and set their own conditions on when an executor can be triggered. Executors can be extended to support this behavior by validating conditions using the ComposableConditionManager
. The ModuleKit currently has conditions for:
- Gas price
- Token price
- Time based schedules
... and more to come! View all conditions here.
Users can select different conditions and in order to support any existing condition, the executor needs to call the ComposableConditionManager
.
...
import { ComposableConditionManager, ConditionConfig } from "modulekit/core/ComposableCondition.sol";
contract GasPriceExecutor is ExecutorBase {
...
mapping(address account => ConditionConfig[] conditions) conditions;
function execute(address account) external {
if (!ComposableConditionManager(manager).checkCondition(account, conditions[account])) revert NotExecutable();
...
}
...
}
Integrations
Integrations are Solidity libraries that can easily be integrated into modules to simplify the developer experience for common use cases. The ModuleKit currently has integrations for:
- Yearn
- ERC-4626 Vaults
- ChainLink
- Uniswap
... and more to come! View all integrations here.
To use an integration, simply import it into your module and call the functions that you need.
...
import {YearnWithdraw} from "modulekit/modulekit/integrations/yearn/YearnWidthdraw.sol";
contract YearnModule is ExecutorBase {
...
function withdrawFromYearn(IExecutorManager manager, address account, address yToken) external {
...
YearnWithdraw.withdraw(manager, account, yToken);
}
...
}
Testing modules
Execute ERC-4337 flow
Execute an ERC-4337 transaction on the account with and address
target and bytes
calldata.
instance.exec4337(address target, bytes memory data);
Execute an ERC-4337 transaction on the account with and address
target, a uint256
value and bytes
calldata.
instance.exec4337(address target, uint256 value, bytes memory data);
Execute an ERC-4337 transaction on the account with and address
target, a uint256
value, bytes
calldata and bytes
signature.
instance.exec4337(address target, uint256 value, bytes memory data, bytes memory signature);
Add and remove modules to the account
Add a validator to the account by providing the address
of the validator.
instance.addValidator(address validator);
Remove a validator to the account by providing the address
of the validator.
instance.removeValidator(address validator);
Check if a validator is enabled on the account by providing the address
of the validator.
instance.isValidatorEnabled(address validator);
Add a session key using the session key manager, by providing the session key config data.
instance.addSessionKey(uint256 validUntil, uint256 validAfter, address sessionValidationModule, bytes memory sessionKeyData);
Add a hook to the account by providing the address
of the hook. (Note: this is not supported on all accounts yet)
instance.addHook(address hook);
Check if a hook is enabled on the account by providing the address
of the hook. (Note: this is not supported on all accounts yet)
instance.isHookEnabled(address hook);
Add an executor to the account by providing the address
of the executor.
instance.addExecutor(address executor);
Remove an executor to the account by providing the address
of the executor.
instance.removeExecutor(address executor);
Check if an executor is enabled on the account by providing the address
of the executor.
instance.isExecutorEnabled(address executor);
Set a condition on the ConditionManager by providing the address
of the executor and the ConditionConfig[]
condition data.
instance.setCondition(address executor, ConditionConfig[] conditions);
Add a fallback handler to the account by providing the bytes4
function signature, a bool
whether the method is static (view) or not and the address
of the handler. (Note: this is not supported on all accounts yet)
instance.addFallback(bytes4 handleFunctionSig, bool isStatic, address handler);
ERC-4337 utils
Get the hash of the UserOperation in order to, for example, sign it.
instance.getUserOpHash(address target, uint256 value, bytes memory callData);
Gets the formatted UserOperation with calldata.
instance.getFormattedUserOp(address target, uint256 value, bytes memory callData);
Encodes the chosen validator for a UserOperation.
instance.encodeValidator(bytes memory signature, address validator);
Expects an ERC-4337 transaction to revert, similar to Foundry's vm.expectRevert()
.
instance.expect4337Revert();
Deploying modules
Deterministically deploy a module using the CREATE2
opcode, by providing the bytes
creation code of the module, bytes
constructor parameters for the module, bytes32
salt vakue to create uniqueness and bytes
additional data to be stored on the Module Registry.
deployModule(bytes memory code, bytes memory deployParams, bytes32 salt, bytes memory data);
Deterministically deploy a module using CREATE3
, by providing the bytes
creation code of the module, bytes
constructor parameters for the module, bytes32
salt vakue to create uniqueness and bytes
additional data to be stored on the Module Registry.
deployModuleCreate3(bytes memory code, bytes memory deployParams, bytes32 salt, bytes memory data);
Deploy a module using an external factory contract, by providing the address
of the factory, bytes
encoded calldata to be sent to the factory and bytes
additional data to be stored on the Module Registry.
deployModuleViaFactory(address factory, bytes memory callOnFactory, bytes memory data);