|
1 | 1 | // SPDX-License-Identifier: MIT
|
2 | 2 | pragma solidity ^0.8.23;
|
3 | 3 |
|
4 |
| -// forge |
| 4 | +// forge-std |
5 | 5 | import { Test } from "forge-std/Test.sol";
|
6 | 6 | // import { console } from "forge-std/console.sol";
|
7 | 7 |
|
@@ -155,7 +155,7 @@ contract SemaphoreValidatorUnitTest is RhinestoneModuleKit, Test {
|
155 | 155 | assertEq(semaphoreValidator.memberCount(smartAcct.account), 0);
|
156 | 156 | assertEq(semaphoreValidator.isInitialized(smartAcct.account), false);
|
157 | 157 |
|
158 |
| - (bool bExist, uint256 groupId) = semaphoreValidator.getGroupId(smartAcct.account); |
| 158 | + (bool bExist,) = semaphoreValidator.getGroupId(smartAcct.account); |
159 | 159 | assertEq(bExist, false);
|
160 | 160 | }
|
161 | 161 |
|
@@ -250,47 +250,155 @@ contract SemaphoreValidatorUnitTest is RhinestoneModuleKit, Test {
|
250 | 250 | userOpData.execUserOps();
|
251 | 251 | }
|
252 | 252 |
|
253 |
| - function test_initiateTokensTransferMemberValid() public setupSmartAcctOneMember { |
| 253 | + function test_initiateTokensTransferMemberValid() |
| 254 | + public |
| 255 | + setupSmartAcctOneMember |
| 256 | + returns (bytes32 txHash) |
| 257 | + { |
254 | 258 | User storage member = $users[0];
|
255 | 259 |
|
256 | 260 | uint256 value = 1 ether;
|
257 | 261 | address targetAddr = $users[1].addr;
|
258 | 262 | uint256 seq = semaphoreValidator.getNextSeqNum(smartAcct.account);
|
| 263 | + txHash = keccak256(abi.encodePacked(seq, targetAddr, value, "")); |
| 264 | + |
| 265 | + { |
| 266 | + // Using scope to limit the number of local variables, work around the `stack too deep` |
| 267 | + // error. |
| 268 | + // Generate the semaphore proof |
| 269 | + (bool bExist, uint256 groupId) = semaphoreValidator.getGroupId(smartAcct.account); |
| 270 | + assert(bExist); |
| 271 | + uint256[] memory members = new uint256[](1); |
| 272 | + members[0] = member.identity.commitment(); |
| 273 | + ISemaphore.SemaphoreProof memory smProof = |
| 274 | + member.identity.generateSempahoreProof(groupId, members, txHash); |
| 275 | + |
| 276 | + // Composing the UserOpData |
| 277 | + UserOpData memory userOpData = smartAcct.getExecOps({ |
| 278 | + target: address(semaphoreValidator), |
| 279 | + value: value, |
| 280 | + callData: abi.encodeCall( |
| 281 | + SemaphoreMSAValidator.initiateTx, (targetAddr, "", smProof, false) |
| 282 | + ), |
| 283 | + txValidator: address(semaphoreValidator) |
| 284 | + }); |
| 285 | + userOpData.userOp.signature = member.identity.signHash(userOpData.userOpHash); |
| 286 | + |
| 287 | + // Expecting `InitiatedTx` event to be emitted |
| 288 | + vm.expectEmit(true, true, true, true, address(semaphoreValidator)); |
| 289 | + emit SemaphoreMSAValidator.InitiatedTx(smartAcct.account, seq, txHash); |
| 290 | + userOpData.execUserOps(); |
| 291 | + } |
259 | 292 |
|
260 |
| - bytes32 txHash = keccak256(abi.encodePacked(seq, targetAddr, value, "")); |
| 293 | + // Test the states are changed accordingly |
| 294 | + assertEq(semaphoreValidator.acctSeqNum(smartAcct.account), seq + 1); |
261 | 295 |
|
262 |
| - // Generate the semaphore proof |
263 |
| - (bool bExist, uint256 groupId) = semaphoreValidator.getGroupId(smartAcct.account); |
264 |
| - assert(bExist); |
265 |
| - uint256[] memory members = new uint256[](1); |
266 |
| - members[0] = member.identity.commitment(); |
267 |
| - ISemaphore.SemaphoreProof memory smProof = |
268 |
| - member.identity.generateSempahoreProof(groupId, members, txHash); |
| 296 | + (address eccTargetAddr, bytes memory eccCallData, uint256 eccValue, uint8 eccCount) = |
| 297 | + semaphoreValidator.acctTxCount(smartAcct.account, txHash); |
| 298 | + |
| 299 | + assertEq(eccTargetAddr, targetAddr); |
| 300 | + assertEq(eccValue, value); |
| 301 | + assertEq(eccCallData, ""); |
| 302 | + assertEq(eccCount, 1); |
| 303 | + } |
| 304 | + |
| 305 | + function test_initiateTokensTransferMemberValidAndExecuteInvalidTxHash() public { |
| 306 | + bytes32 forgedHash = test_initiateTokensTransferMemberValid(); |
| 307 | + // Changed the last 2 bytes to 0xffff |
| 308 | + forgedHash = forgedHash | bytes32(uint256(65_535)); |
| 309 | + |
| 310 | + User storage member = $users[0]; |
| 311 | + |
| 312 | + // Now execute the token transfer. |
| 313 | + // Composing the UserOpData. |
| 314 | + UserOpData memory userOpData = smartAcct.getExecOps({ |
| 315 | + target: address(semaphoreValidator), |
| 316 | + value: 0, |
| 317 | + callData: abi.encodeCall(SemaphoreMSAValidator.executeTx, (forgedHash)), |
| 318 | + txValidator: address(semaphoreValidator) |
| 319 | + }); |
| 320 | + userOpData.userOp.signature = member.identity.signHash(userOpData.userOpHash); |
| 321 | + |
| 322 | + smartAcct.expect4337Revert( |
| 323 | + abi.encodeWithSelector( |
| 324 | + SemaphoreMSAValidator.TxHashNotFound.selector, smartAcct.account, forgedHash |
| 325 | + ) |
| 326 | + ); |
| 327 | + userOpData.execUserOps(); |
| 328 | + } |
| 329 | + |
| 330 | + function test_ExecuteTxFailure() public pure { |
| 331 | + revert("to be implemented"); |
| 332 | + } |
269 | 333 |
|
270 |
| - // Composing the UserOpData |
| 334 | + function test_initiateTokensTransferMemberValidAndExecuteValid() public { |
| 335 | + bytes32 txHash = test_initiateTokensTransferMemberValid(); |
| 336 | + |
| 337 | + User storage member = $users[0]; |
| 338 | + address targetAddr = $users[1].addr; |
| 339 | + uint256 value = 1 ether; |
| 340 | + uint256 beforeBalance = targetAddr.balance; |
| 341 | + |
| 342 | + // Now execute the token transfer. |
| 343 | + // Composing the UserOpData. |
271 | 344 | UserOpData memory userOpData = smartAcct.getExecOps({
|
272 | 345 | target: address(semaphoreValidator),
|
273 |
| - value: value, |
274 |
| - callData: abi.encodeCall(SemaphoreMSAValidator.initiateTx, (targetAddr, "", smProof, false)), |
| 346 | + value: 0, |
| 347 | + callData: abi.encodeCall(SemaphoreMSAValidator.executeTx, (txHash)), |
275 | 348 | txValidator: address(semaphoreValidator)
|
276 | 349 | });
|
277 | 350 | userOpData.userOp.signature = member.identity.signHash(userOpData.userOpHash);
|
278 | 351 |
|
279 |
| - // Expecting `InitiatedTx` event to be emitted |
| 352 | + // Test event emission |
280 | 353 | vm.expectEmit(true, true, true, true, address(semaphoreValidator));
|
281 |
| - emit SemaphoreMSAValidator.InitiatedTx(smartAcct.account, seq, txHash); |
| 354 | + emit SemaphoreMSAValidator.ExecutedTx(smartAcct.account, txHash); |
282 | 355 | userOpData.execUserOps();
|
283 | 356 |
|
284 |
| - // Test the states are changed accordingly |
285 |
| - assertEq(semaphoreValidator.acctSeqNum(smartAcct.account), seq + 1); |
| 357 | + uint256 afterBalance = targetAddr.balance; |
| 358 | + assertEq(afterBalance - beforeBalance, value); |
| 359 | + } |
286 | 360 |
|
287 |
| - (address eccTargetAddr, bytes memory eccCallData, uint256 eccValue, uint8 eccCount) = |
288 |
| - semaphoreValidator.acctTxCount(smartAcct.account, txHash); |
| 361 | + function test_initiateTokensTransferMemberAndExecuteValid() public setupSmartAcctOneMember { |
| 362 | + User storage member = $users[0]; |
| 363 | + uint256 value = 1 ether; |
| 364 | + address targetAddr = $users[1].addr; |
| 365 | + uint256 beforeBalance = targetAddr.balance; |
| 366 | + uint256 seq = semaphoreValidator.getNextSeqNum(smartAcct.account); |
| 367 | + bytes32 txHash = keccak256(abi.encodePacked(seq, targetAddr, value, "")); |
289 | 368 |
|
290 |
| - assertEq(eccTargetAddr, targetAddr); |
291 |
| - assertEq(eccValue, value); |
292 |
| - assertEq(eccCallData, ""); |
293 |
| - assertEq(eccCount, 1); |
| 369 | + { |
| 370 | + // Using scope to limit the number of local variables, work around the `stack too deep` |
| 371 | + // error. |
| 372 | + // Generate the semaphore proof |
| 373 | + (bool bExist, uint256 groupId) = semaphoreValidator.getGroupId(smartAcct.account); |
| 374 | + assert(bExist); |
| 375 | + uint256[] memory members = new uint256[](1); |
| 376 | + members[0] = member.identity.commitment(); |
| 377 | + ISemaphore.SemaphoreProof memory smProof = |
| 378 | + member.identity.generateSempahoreProof(groupId, members, txHash); |
| 379 | + |
| 380 | + // Composing the UserOpData |
| 381 | + UserOpData memory userOpData = smartAcct.getExecOps({ |
| 382 | + target: address(semaphoreValidator), |
| 383 | + value: value, |
| 384 | + callData: abi.encodeCall( |
| 385 | + SemaphoreMSAValidator.initiateTx, (targetAddr, "", smProof, true) |
| 386 | + ), |
| 387 | + txValidator: address(semaphoreValidator) |
| 388 | + }); |
| 389 | + userOpData.userOp.signature = member.identity.signHash(userOpData.userOpHash); |
| 390 | + |
| 391 | + // Expecting `InitiatedTx` event to be emitted |
| 392 | + vm.expectEmit(true, true, true, true, address(semaphoreValidator)); |
| 393 | + emit SemaphoreMSAValidator.InitiatedTx(smartAcct.account, seq, txHash); |
| 394 | + vm.expectEmit(true, true, true, true, address(semaphoreValidator)); |
| 395 | + emit SemaphoreMSAValidator.ExecutedTx(smartAcct.account, txHash); |
| 396 | + userOpData.execUserOps(); |
| 397 | + } |
| 398 | + |
| 399 | + // Confirm user balance has changed |
| 400 | + uint256 afterBalance = targetAddr.balance; |
| 401 | + assertEq(afterBalance - beforeBalance, value); |
294 | 402 | }
|
295 | 403 |
|
296 | 404 | function test_initiateTxOneMemberNonValidatorCall()
|
|
0 commit comments