1/5
## Account Abstraction Lesson 13: Test Validate UserOps In this lesson, we are going to write another test called `testValidationOfUserOps`. We want to be able to do three things here: 1. Sign `userOps` 2. Call validate `userOps` 3. Assert the return is correct Let's get started! --- ### Arrange In our test function, we can simply copy and paste the Arrange from `testRecoverSignedOp`. **<span style="color:red">MinimalAccountTest.t.sol</span>** ```js function testValidationOfUserOps() public { // Arrange assertEq(usdc.balanceOf(address(minimalAccount)), 0); address dest = address(usdc); uint256 value = 0; bytes memory functionData = abi.encodeWithSelector(ERC20Mock.mint.selector, address(minimalAccount), AMOUNT); bytes memory executeCallData = abi.encodeWithSelector(MinimalAccount.execute.selector, dest, value, functionData); PackedUserOperation memory packedUserOp = sendPackedUserOp.generateSignedUserOperation( executeCallData, helperConfig.getConfig()); bytes32 userOperationHash = IEntryPoint(helperConfig.getConfig().entryPoint).getUserOpHash(packedUserOp); } ``` --- ### Act In our **Act**, we want to make sure that `validateUserOp` returns correctly. If you look back at this function in our `MinimalAccount`, you will notice that it can only be called by the `EntryPoint`. So, in our `vm.prank`, we are going to be the `EntryPoint`. You will also notice that it takes a `userOp`, `userOpHash`, and `missingAccountFunds`. We'll need to set this to our `validationData`. We already have `packedUserOp` and `userOperationHash` in our **Arrange**. We will need to add `missingAccountFunds` there as well. Let's make it a `uint256` and set it to 1e18 **(to simulate the amount of funds that are missing from the account)**. Lastly, we remember that our `SIG_VALIDATION_SUCCESS` = 0. So, we'll assume this will be the case in this test. ```js uint256 missingAccountFunds = 1e18; // Act vm.prank(helperConfig.getConfig().entryPoint); uint256 validationData = minimalAccount.validateUserOp(packedUserOp, userOperationHash, missingAccountFunds); assertEq(validationData, 0); ``` With that, our function should now look like this. ```js function testValidationOfUserOps() public { // Arrange assertEq(usdc.balanceOf(address(minimalAccount)), 0); address dest = address(usdc); uint256 value = 0; bytes memory functionData = abi.encodeWithSelector(ERC20Mock.mint.selector, address(minimalAccount), AMOUNT); bytes memory executeCallData = abi.encodeWithSelector(MinimalAccount.execute.selector, dest, value, functionData); PackedUserOperation memory packedUserOp = sendPackedUserOp.generateSignedUserOperation( executeCallData, helperConfig.getConfig()); bytes32 userOperationHash = IEntryPoint(helperConfig.getConfig().entryPoint).getUserOpHash(packedUserOp); uint256 missingAccountFunds = 1e18; // Act vm.prank(helperConfig.getConfig().entryPoint); uint256 validationData = minimalAccount.validateUserOp(packedUserOp, userOperationHash, missingAccountFunds); assertEq(validationData, 0); } ``` Let's test it. ```js forge test --mt testValidationOfUserOps -vvv ``` It passes! We are on a roll! Now we know that we are getting the correct signatures and that our validation is working properly. Next, we get to see if our EntryPoint can execute commands. Before you move on, consider these review questions. --- ### Questions for Review <summary>1. What is the main objective of the testValidationOfUserOps?</summary> <details> **<summary><span style="color:red">Click for Answers</span></summary>** The main objective is to sign userOps, call validateUserOp, and assert that the return value is correct. </details> <summary>2. Why do we set missingAccountFunds to 1e18?</summary> <details> **<summary><span style="color:red">Click for Answers</span></summary>** It simulates the amount of funds that are missing from the account, which is required for the validateUserOp function. </details> <summary>3. What does the assertEq(validationData, 0) statement check for?</summary> <details> **<summary><span style="color:red">Click for Answers</span></summary>** It checks that the validateUserOp function returns 0, indicating that the signature validation was successful. </details>
Testing the validateUserOps function to check the validation is working correctly.
Previous lesson
Previous
Next lesson
Next
Give us feedback
Course Overview
About the course
Advanced smart contract development
How to develop a stablecoin
How to develop a DeFi protocol
How to develop a DAO
Advanced smart contracts testing
Fuzz testing
Manual verification
Web3 Developer Relations
$85,000 - $125,000 (avg. salary)
Web3 developer
$60,000 - $150,000 (avg. salary)
Smart Contract Engineer
$100,000 - $150,000 (avg. salary)
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
Security researcher
$49,999 - $120,000 (avg. salary)
Guest lecturers:
Juliette Chevalier
Lead Developer relations at Aragon
Nader Dabit
Director of developer relations at Avara
Ally Haire
Developer relations at Protocol Labs
Harrison
Founder at GasliteGG
Last updated on January 31, 2025
Solidity Developer
Advanced FoundryDuration: 36min
Duration: 3h 06min
Duration: 5h 02min
Duration: 5h 50min
Duration: 2h 47min
Duration: 1h 23min
Duration: 4h 28min
Duration: 1h 19min
Duration: 1h 10min
Course Overview
About the course
Advanced smart contract development
How to develop a stablecoin
How to develop a DeFi protocol
How to develop a DAO
Advanced smart contracts testing
Fuzz testing
Manual verification
Web3 Developer Relations
$85,000 - $125,000 (avg. salary)
Web3 developer
$60,000 - $150,000 (avg. salary)
Smart Contract Engineer
$100,000 - $150,000 (avg. salary)
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
Security researcher
$49,999 - $120,000 (avg. salary)
Guest lecturers:
Juliette Chevalier
Lead Developer relations at Aragon
Nader Dabit
Director of developer relations at Avara
Ally Haire
Developer relations at Protocol Labs
Harrison
Founder at GasliteGG
Last updated on January 31, 2025
Testimonials
Read what our students have to say about this course.
Chainlink
Chainlink
Gustavo Gonzalez
Solutions Engineer at OpenZeppelin
Francesco Andreoli
Lead Devrel at Metamask
Albert Hu
DeForm Founding Engineer
Radek
Senior Developer Advocate at Ceramic
Boidushya
WalletConnect
Idris
Developer Relations Engineer at Axelar