Module System
Extending the interpreter with pluggable custom logic.
Matador uses a modular architecture to keep the core interpreter lightweight while enabling unlimited extensibility. Opcodes are grouped into "Modules," which are external contracts called by the interpreter when needed.
Architecture
Module Dispatch
When the interpreter encounters an opcode:
- It identifies the Module ID associated with that opcode (e.g.,
SIGNATURE_CHECKbelongs to Module 1). - It queries the
ModuleRegistryfor the address of that module. - It invokes the module via
staticcall(for view/pure checks) ordelegatecall(for stateful checks).
IPermissionModule Interface
All modules must implement the IPermissionModule interface.
interface IPermissionModule {
enum ExecutionMode { View, Mutable }
function evaluate(
bytes memory context,
uint256 offset,
address subject,
ExecutionContext calldata exec,
uint8 opcode,
ExecutionMode mode
) external returns (uint256 newOffset, bool success);
}Parameters
| Parameter | Description |
|---|---|
context | The full bytecode of the policy. |
offset | The current pointer position in the bytecode (arguments start here). |
subject | The smart account being controlled. |
exec | Runtime transaction metadata (target, value, etc.). |
opcode | The specific opcode triggering this call. |
mode | View (read-only) or Mutable (can write state). |
Standard Modules
Matador ships with a set of verified standard modules.
| ID | Name | Functionality |
|---|---|---|
| 0 | Core | Arithmetic, context inspection, whitelists. (Built-in) |
| 1 | Delegation | Signature validation (SIGNATURE_CHECK), operation counting. |
| 2 | Storage | Advanced storage inspection (SLOAD, SSTORE). |
| 3 | Execution | Call depth, reentrancy guards. |
| 4 | Code | Code hash verification (EXTCODEHASH_CHECK). |
| 5 | Consensus | Block difficulty, coinbase, randomness. |
| 6 | Blob | EIP-4844 data availability checks. |
Creating a Custom Module
You can build your own module to implement domain-specific logic, such as checking a Chainlink oracle or verifying a Merkle proof.
Implement the Interface
Create a contract inheriting IPermissionModule.
contract OracleModule is IPermissionModule {
using ContextReader for bytes;
function evaluate(
bytes memory context,
uint256 offset,
address,
ExecutionContext calldata,
uint8 opcode,
ExecutionMode
) external view returns (uint256, bool) {
if (opcode == MY_ORACLE_OPCODE) {
// Read args from bytecode
(uint256 minPrice, newOffset) = context.readUint256(offset);
// Perform check
uint256 price = Oracle.latestAnswer();
return (newOffset, price >= minPrice);
}
revert("Unknown Opcode");
}
}Register Module
Deploy your contract and register it with the ModuleRegistry under a unique ID (e.g., ID 10).
Author Policy
Use the CUSTOM_CHECK opcode or update the compiler to recognize your new opcode.