Tutorials
Building modules
This tutorial will walk you through building a simple module. You can choose what kind of module you want to build. For more information about the different kinds of modules, check out the modules section of the docs.
This tutorial uses Foundry, a toolchain to simplify and speed up smart contract development. If you are not familiar with Foundry, feel free to check out their docs.
If you do not have foundry installed, run the following command and follow the onscreen instructions: bash curl -L https://foundry.paradigm.xyz | bash
Installation
- Get started with ModuleKit by using our template or adding it into an existing foundry project:
Installation
git clone https://github.com/rhinestonewtf/module-template.git
cd module-template
forge install
Building a module
In this section, we will walk you through building a simple validator module. This validator module allows the owner of an account to sign transactions using an EOA.
- Duplicate the file
src/validators/ValidatorTemplate.sol
and rename it toSimpleValidator.sol
and the contract toSimpleValidator
.
If you added the ModuleKit to an existing project, you will need to create the SimpleValidator.sol
file yourself
and copy the code from
here.
- Add the following import statement to the top of the file:
import {ECDSA} from "solady/utils/ECDSA.sol";
and add the following code to the top of the contract:
contract SimpleValidator is BaseValidator {
using ECDSA for bytes32;
mapping(address => address) public owners;
function setOwner(address owner) external {
owners[msg.sender] = owner;
}
...
}
- Edit the contract functions to match the following code:
validateUserOp
function validateUserOp(UserOperation calldata userOp, bytes32 userOpHash)
external
view
override
returns (uint256)
{
bytes32 hash = userOpHash.toEthSignedMessageHash();
(bytes memory sig, ) = abi.decode(userOp.signature, (bytes, address));
if (owners[msg.sender] != hash.recover(sig)) {
return VALIDATION_FAILED;
}
return VALIDATION_SUCCESS;
}
This function validates the userOperation by checking that the signature is valid and made by the owner of the account.
isValidSignature
function isValidSignature(bytes32 signedDataHash, bytes memory moduleSignature)
public
view
override
returns (bytes4)
{
bytes32 hash = signedDataHash.toEthSignedMessageHash();
if (owners[msg.sender] != hash.recover(moduleSignature)) {
return 0xffffffff;
}
return ERC1271_MAGICVALUE;
}
This function checks that the signature is valid and made by the owner of the account. This allows the validator to be used as an EIP-1271 validator.