Skip to content

Commit cd331f5

Browse files
authored
Add forge tests (#1)
* Add tests * Add github action
1 parent f1775a1 commit cd331f5

File tree

2 files changed

+137
-0
lines changed

2 files changed

+137
-0
lines changed

.github/workflows/tests.yml

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: tests
2+
on:
3+
push:
4+
branches:
5+
- "main"
6+
pull_request:
7+
branches:
8+
- "main"
9+
jobs:
10+
check:
11+
name: Foundry project
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v3
15+
with:
16+
submodules: recursive
17+
- name: Install Foundry
18+
uses: foundry-rs/foundry-toolchain@v1
19+
- name: Run tests
20+
run: forge test -vvv

test/Paymaster.t.sol

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity ^0.8.13;
3+
4+
import {Test, console} from "forge-std/Test.sol";
5+
import {Paymaster} from "../src/Paymaster.sol";
6+
import {IEntryPoint} from "@account-abstraction/interfaces/IEntryPoint.sol";
7+
import {IStakeManager} from "@account-abstraction/interfaces/IStakeManager.sol";
8+
import {EntryPoint} from "@account-abstraction/core/EntryPoint.sol";
9+
import {UserOperation} from "@account-abstraction/interfaces/UserOperation.sol";
10+
import {SimpleAccountFactory} from "@account-abstraction/samples/SimpleAccountFactory.sol";
11+
import {SimpleAccount} from "@account-abstraction/samples/SimpleAccount.sol";
12+
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
13+
14+
contract PaymasterTest is Test {
15+
EntryPoint public entrypoint;
16+
Paymaster public paymaster;
17+
SimpleAccount public account;
18+
19+
uint48 constant MOCK_VALID_UNTIL = 0x00000000deadbeef;
20+
uint48 constant MOCK_VALID_AFTER = 0x0000000000001234;
21+
bytes constant MOCK_SIG = "0x1234";
22+
address constant PAYMASTER_SIGNER = 0xC3Bf2750F0d47098f487D45b2FB26b32eCbAf9a2;
23+
uint256 constant PAYMASTER_SIGNER_KEY = 0x6a6c11c6f4703865cc4a88c6ebf0a605fdeeccd8052d66101d1d02730740a3c0;
24+
address constant ACCOUNT_OWNER = 0x39c0Bb04Bf6B779ac994f6A5211204e3Dbe16741;
25+
uint256 constant ACCOUNT_OWNER_KEY = 0x4034df11fcc455209edcb8948449a4dff732376dab6d03dc2d099d0084b0f023;
26+
27+
function setUp() public {
28+
entrypoint = new EntryPoint();
29+
paymaster = new Paymaster(entrypoint, PAYMASTER_SIGNER);
30+
SimpleAccountFactory factory = new SimpleAccountFactory(entrypoint);
31+
account = factory.createAccount(ACCOUNT_OWNER, 0);
32+
}
33+
34+
function test_parsePaymasterAndData() public {
35+
bytes memory paymasterAndData = abi.encodePacked(address(paymaster), abi.encode(MOCK_VALID_UNTIL, MOCK_VALID_AFTER), MOCK_SIG);
36+
(uint48 validUntil, uint48 validAfter, bytes memory signature) = paymaster.parsePaymasterAndData(paymasterAndData);
37+
assertEq(validUntil, MOCK_VALID_UNTIL);
38+
assertEq(validAfter, MOCK_VALID_AFTER);
39+
assertEq(signature, MOCK_SIG);
40+
}
41+
42+
function test_validatePaymasterUserOpValidSignature() public {
43+
UserOperation memory userOp = createUserOp();
44+
signUserOp(userOp);
45+
46+
vm.expectRevert(createEncodedValidationResult(false));
47+
entrypoint.simulateValidation(userOp);
48+
}
49+
50+
function test_validatePaymasterUserOpWrongSigner() public {
51+
UserOperation memory userOp = createUserOp();
52+
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ACCOUNT_OWNER_KEY, ECDSA.toEthSignedMessageHash(paymaster.getHash(userOp, MOCK_VALID_UNTIL, MOCK_VALID_AFTER)));
53+
userOp.paymasterAndData = abi.encodePacked(address(paymaster), abi.encode(MOCK_VALID_UNTIL, MOCK_VALID_AFTER), r, s, v);
54+
signUserOp(userOp);
55+
56+
vm.expectRevert(createEncodedValidationResult(true));
57+
entrypoint.simulateValidation(userOp);
58+
}
59+
60+
function test_validatePaymasterUserOpNoSignature() public {
61+
UserOperation memory userOp = createUserOp();
62+
userOp.paymasterAndData = abi.encodePacked(address(paymaster), abi.encode(MOCK_VALID_UNTIL, MOCK_VALID_AFTER));
63+
signUserOp(userOp);
64+
65+
vm.expectRevert(
66+
abi.encodeWithSelector(
67+
IEntryPoint.FailedOp.selector,
68+
0, "AA33 reverted: Paymaster: invalid signature length in paymasterAndData"
69+
)
70+
);
71+
entrypoint.simulateValidation(userOp);
72+
}
73+
74+
function test_validatePaymasterUserOpInvalidSignature() public {
75+
UserOperation memory userOp = createUserOp();
76+
userOp.paymasterAndData = abi.encodePacked(address(paymaster), abi.encode(MOCK_VALID_UNTIL, MOCK_VALID_AFTER), bytes32(0), bytes32(0), uint8(0));
77+
signUserOp(userOp);
78+
79+
vm.expectRevert(
80+
abi.encodeWithSelector(
81+
IEntryPoint.FailedOp.selector,
82+
0, "AA33 reverted: ECDSA: invalid signature"
83+
)
84+
);
85+
entrypoint.simulateValidation(userOp);
86+
}
87+
88+
function createUserOp() public view returns (UserOperation memory) {
89+
UserOperation memory userOp;
90+
userOp.sender = address(account);
91+
userOp.verificationGasLimit = 100000;
92+
(uint8 v, bytes32 r, bytes32 s) = vm.sign(PAYMASTER_SIGNER_KEY, ECDSA.toEthSignedMessageHash(paymaster.getHash(userOp, MOCK_VALID_UNTIL, MOCK_VALID_AFTER)));
93+
userOp.paymasterAndData = abi.encodePacked(address(paymaster), abi.encode(MOCK_VALID_UNTIL, MOCK_VALID_AFTER), r, s, v);
94+
return userOp;
95+
}
96+
97+
function signUserOp(UserOperation memory userOp) public view {
98+
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ACCOUNT_OWNER_KEY, ECDSA.toEthSignedMessageHash(entrypoint.getUserOpHash(userOp)));
99+
userOp.signature = abi.encodePacked(r, s, v);
100+
}
101+
102+
function createEncodedValidationResult(bool sigFailed) public pure returns (bytes memory) {
103+
uint256 preOpGas = 55114;
104+
if (sigFailed) {
105+
preOpGas = 55120;
106+
}
107+
uint256 prefund = 0;
108+
bytes memory paymasterContext = "";
109+
return abi.encodeWithSelector(
110+
IEntryPoint.ValidationResult.selector,
111+
IEntryPoint.ReturnInfo(preOpGas, prefund, sigFailed, MOCK_VALID_AFTER, MOCK_VALID_UNTIL, paymasterContext),
112+
IStakeManager.StakeInfo(0, 0),
113+
IStakeManager.StakeInfo(0, 0),
114+
IStakeManager.StakeInfo(0, 0)
115+
);
116+
}
117+
}

0 commit comments

Comments
 (0)