Skip to content

Commit a86f15c

Browse files
feat: fallback module tests added
1 parent 20495c8 commit a86f15c

16 files changed

+1034
-0
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.28;
3+
4+
import {IModule} from '../../../src/interfaces/IERC7579Module.sol';
5+
import {EncodedModuleTypes} from '../../../src/lib/ModuleTypeLib.sol';
6+
import '../../../src/types/Constants.sol';
7+
8+
contract EmittingHook is IModule {
9+
event PreCheckMsgData(bytes data);
10+
event PreCheckExtractedSelector(bytes4 selector);
11+
event PreCheckSender(address sender);
12+
event PreCheckValue(uint256 value);
13+
event HookOnInstallCalled(bytes32 dataFirstWord);
14+
event PostCheckCalled();
15+
16+
function onInstall(bytes calldata data) external override {
17+
if (data.length >= 0x20) {
18+
emit HookOnInstallCalled(bytes32(data[0:32]));
19+
}
20+
}
21+
22+
function onUninstall(bytes calldata) external override {
23+
emit PostCheckCalled();
24+
}
25+
26+
function preCheck(address sender, uint256 value, bytes calldata data) external returns (bytes memory) {
27+
bytes4 selector = bytes4(data[0:4]);
28+
emit PreCheckExtractedSelector(selector);
29+
emit PreCheckMsgData(data);
30+
emit PreCheckSender(sender);
31+
emit PreCheckValue(value);
32+
33+
return '';
34+
}
35+
36+
function postCheck(bytes calldata hookData) external {
37+
hookData;
38+
emit PostCheckCalled();
39+
}
40+
41+
function isModuleType(uint256 moduleTypeId) external pure returns (bool) {
42+
return moduleTypeId == MODULE_TYPE_HOOK;
43+
}
44+
45+
function isInitialized(address) external pure returns (bool) {
46+
return false;
47+
}
48+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.27;
3+
4+
import {StartaleSmartAccount} from '../../../src/StartaleSmartAccount.sol';
5+
import {IERC7579Account} from '../../../src/interfaces/IERC7579Account.sol';
6+
7+
interface IExposed7702SmartAccount is IERC7579Account {
8+
function amIERC7702() external view returns (bool);
9+
}
10+
11+
contract Exposed7702SmartAccount is StartaleSmartAccount, IExposed7702SmartAccount {
12+
constructor(
13+
address anEntryPoint,
14+
address defaultValidator,
15+
bytes memory initData
16+
) StartaleSmartAccount(anEntryPoint, defaultValidator, initData) {}
17+
18+
function amIERC7702() external view returns (bool) {
19+
return _amIERC7702();
20+
}
21+
}

test/foundry/mocks/MockHandler.sol

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,94 @@
1+
// SPDX-License-Identifier: LGPL-3.0-only
2+
pragma solidity ^0.8.28;
13

4+
import {IFallback} from '../../../src/interfaces/IERC7579Module.sol';
5+
import {MODULE_TYPE_FALLBACK} from '../../../src/types/Constants.sol';
6+
7+
contract MockHandler is IFallback {
8+
uint256 public count;
9+
string constant NAME = 'Default Handler';
10+
string constant VERSION = '1.0.0';
11+
12+
event GenericFallbackCalled(address sender, uint256 value, bytes data); // Event for generic fallback
13+
event HandlerOnInstallCalled(bytes32 dataFirstWord);
14+
15+
error NonExistingMethodCalled(bytes4 selector);
16+
error RevertError();
17+
18+
fallback() external {
19+
revert NonExistingMethodCalled(msg.sig);
20+
}
21+
22+
// Example function to manually trigger the fallback mechanism
23+
function onGenericFallback(address sender, uint256 value, bytes calldata data) external returns (bytes4) {
24+
emit GenericFallbackCalled(sender, value, data);
25+
return this.onGenericFallback.selector;
26+
}
27+
28+
function complexReturnData(
29+
string memory input,
30+
bytes4 selector
31+
) external view returns (uint256, bytes memory, address, uint64, address) {
32+
return (
33+
uint256(block.timestamp),
34+
abi.encode(input, NAME, VERSION, selector),
35+
address(this),
36+
uint64(block.chainid),
37+
_msgSender()
38+
);
39+
}
40+
41+
function returnBytes() external view returns (bytes memory) {
42+
return abi.encodePacked(NAME, VERSION);
43+
}
44+
45+
function onInstall(bytes calldata data) external override {
46+
if (data.length >= 0x20) {
47+
emit HandlerOnInstallCalled(bytes32(data[0:32]));
48+
}
49+
}
50+
51+
function onUninstall(bytes calldata data) external override {}
52+
53+
function isModuleType(uint256 moduleTypeId) external pure override returns (bool) {
54+
return moduleTypeId == MODULE_TYPE_FALLBACK;
55+
}
56+
57+
function isInitialized(address) external pure override returns (bool) {
58+
return false;
59+
}
60+
61+
function stateChangingFunction() external {
62+
count++;
63+
}
64+
65+
function successFunction() external pure returns (bytes32) {
66+
return keccak256('SUCCESS');
67+
}
68+
69+
function revertingFunction() external pure {
70+
revert RevertError();
71+
}
72+
73+
function getState() external view returns (uint256) {
74+
return count;
75+
}
76+
77+
function getName() external pure returns (string memory) {
78+
return NAME;
79+
}
80+
81+
function getVersion() external pure returns (string memory) {
82+
return VERSION;
83+
}
84+
85+
function _msgSender() internal pure returns (address sender) {
86+
// The assembly code is more direct than the Solidity version using `abi.decode`.
87+
/* solhint-disable no-inline-assembly */
88+
/// @solidity memory-safe-assembly
89+
assembly {
90+
sender := shr(96, calldataload(sub(calldatasize(), 20)))
91+
}
92+
/* solhint-enable no-inline-assembly */
93+
}
94+
}

test/foundry/mocks/MockHook.sol

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,60 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.28;
13

4+
import {IModule} from '../../../src/interfaces/IERC7579Module.sol';
5+
import {EncodedModuleTypes} from '../../../src/lib/ModuleTypeLib.sol';
6+
import '../../../src/types/Constants.sol';
7+
8+
contract MockHook is IModule {
9+
event PreCheckCalled();
10+
event PostCheckCalled();
11+
event HookOnInstallCalled(bytes32 dataFirstWord);
12+
13+
error PreCheckFailedError();
14+
15+
uint256 public a;
16+
17+
function onInstall(bytes calldata data) external override {
18+
if (data.length >= 0x20) {
19+
emit HookOnInstallCalled(bytes32(data[0:32]));
20+
}
21+
}
22+
23+
function onUninstall(bytes calldata) external override {
24+
emit PostCheckCalled();
25+
}
26+
27+
function preCheck(address sender, uint256 value, bytes calldata data) external returns (bytes memory) {
28+
emit PreCheckCalled();
29+
30+
a++;
31+
32+
// Add a condition to revert if the sender is the zero address or if the value is 1 ether for testing purposes
33+
if (value == 1 ether) {
34+
revert PreCheckFailedError();
35+
}
36+
37+
return '';
38+
}
39+
40+
function postCheck(bytes calldata hookData) external {
41+
hookData;
42+
emit PostCheckCalled();
43+
}
44+
45+
function isModuleType(uint256 moduleTypeId) external pure returns (bool) {
46+
return moduleTypeId == MODULE_TYPE_HOOK;
47+
}
48+
49+
function isInitialized(address) external pure returns (bool) {
50+
return false;
51+
}
52+
53+
function getA() external view returns (uint256) {
54+
return a;
55+
}
56+
57+
function cleanA() external {
58+
a = 0;
59+
}
60+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.23;
3+
4+
import {IERC7579Account} from '../../../src/interfaces/IERC7579Account.sol';
5+
import {IModule} from '../../../src/interfaces/IERC7579Module.sol';
6+
import {ExecutionLib} from '../../../src/lib/ExecutionLib.sol';
7+
import {ModeLib} from '../../../src/lib/ModeLib.sol';
8+
import {EncodedModuleTypes} from '../../../src/lib/ModuleTypeLib.sol';
9+
import '../../../src/types/Constants.sol';
10+
import {Execution} from '../../../src/types/Structs.sol';
11+
import {MessageHashUtils} from '@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol';
12+
import {PackedUserOperation} from 'account-abstraction/interfaces/PackedUserOperation.sol';
13+
import {ECDSA} from 'solady/utils/ECDSA.sol';
14+
import {SignatureCheckerLib} from 'solady/utils/SignatureCheckerLib.sol';
15+
16+
contract MockMultiModule is IModule {
17+
error WrongUninstallData();
18+
19+
mapping(uint256 moduleTypeId => mapping(address smartAccount => bytes32 initData)) configs;
20+
21+
function validateUserOp(
22+
PackedUserOperation calldata userOp,
23+
bytes32 userOpHash
24+
) external view returns (uint256 validation) {
25+
address owner = address(bytes20(configs[MODULE_TYPE_VALIDATOR][msg.sender]));
26+
return ECDSA.recover(MessageHashUtils.toEthSignedMessageHash(userOpHash), userOp.signature) == owner
27+
? VALIDATION_SUCCESS
28+
: VALIDATION_FAILED;
29+
}
30+
31+
function someFallbackFunction(Execution calldata execution) external {
32+
IERC7579Account(msg.sender).executeFromExecutor{value: execution.value}({
33+
mode: ModeLib.encodeSimpleSingle(),
34+
executionCalldata: ExecutionLib.encodeSingle(execution.target, execution.value, execution.callData)
35+
});
36+
}
37+
38+
function getConfig(address smartAccount, uint256 moduleTypeId) external view returns (bytes32) {
39+
return configs[moduleTypeId][smartAccount];
40+
}
41+
42+
function onInstall(bytes calldata data) external override {
43+
if (data.length >= 0x21) {
44+
uint256 moduleTypeId = uint256(uint8(bytes1(data[:1])));
45+
configs[moduleTypeId][msg.sender] = bytes32(data[1:33]);
46+
} else {}
47+
}
48+
49+
function onUninstall(bytes calldata data) external override {
50+
if (data.length >= 0x1) {
51+
uint256 moduleTypeId = uint256(uint8(bytes1(data[:1])));
52+
configs[moduleTypeId][msg.sender] = bytes32(0x00);
53+
} else {
54+
revert WrongUninstallData();
55+
}
56+
}
57+
58+
function preCheck(address, uint256, bytes calldata) external returns (bytes memory) {}
59+
60+
function postCheck(bytes calldata hookData) external {}
61+
62+
function isModuleType(uint256 moduleTypeId) external pure returns (bool) {
63+
return (
64+
moduleTypeId == MODULE_TYPE_HOOK || moduleTypeId == MODULE_TYPE_EXECUTOR || moduleTypeId == MODULE_TYPE_VALIDATOR
65+
|| moduleTypeId == MODULE_TYPE_FALLBACK
66+
);
67+
}
68+
69+
function isInitialized(address smartAccount) external view returns (bool) {
70+
return (
71+
configs[MODULE_TYPE_VALIDATOR][smartAccount] != bytes32(0x00)
72+
|| configs[MODULE_TYPE_EXECUTOR][smartAccount] != bytes32(0x00)
73+
|| configs[MODULE_TYPE_HOOK][smartAccount] != bytes32(0x00)
74+
|| configs[MODULE_TYPE_FALLBACK][smartAccount] != bytes32(0x00)
75+
);
76+
}
77+
}

test/foundry/mocks/MockNFT.sol

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity ^0.8.28;
3+
4+
import '@openzeppelin/contracts/token/ERC1155/ERC1155.sol';
5+
import '@openzeppelin/contracts/token/ERC721/ERC721.sol';
6+
7+
contract MockNFT is ERC721 {
8+
constructor(string memory name, string memory symbol) ERC721(name, symbol) {}
9+
10+
// Mint a new NFT token to the specified address with the specified tokenId
11+
// Warning: This function is only for testing purposes and should not be used in production
12+
function mint(address to, uint256 tokenId) public {
13+
_mint(to, tokenId);
14+
}
15+
16+
// Safely mint a new NFT token to the specified address with the specified tokenId
17+
// Warning: This function is only for testing purposes and should not be used in production
18+
function safeMint(address to, uint256 tokenId) public {
19+
_safeMint(to, tokenId);
20+
}
21+
22+
function safeTransfer(address to, uint256 tokenId) public {
23+
_safeTransfer(msg.sender, to, tokenId);
24+
}
25+
}
26+
27+
contract MockERC1155 is ERC1155 {
28+
constructor(string memory uri) ERC1155(uri) {}
29+
30+
function safeMint(address to, uint256 tokenId, uint256 amount) public {
31+
_mint(to, tokenId, amount, '');
32+
}
33+
34+
function safeMintBatch(address to, uint256[] memory tokenIds, uint256[] memory amounts) public {
35+
_mintBatch(to, tokenIds, amounts, '');
36+
}
37+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,9 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.28;
13

4+
import {IEntryPoint} from 'account-abstraction/interfaces/IEntryPoint.sol';
5+
import {VerifyingPaymaster} from 'account-abstraction/samples/VerifyingPaymaster.sol';
6+
7+
contract MockPaymaster is VerifyingPaymaster {
8+
constructor(address _entryPoint, address _signer) VerifyingPaymaster(IEntryPoint(_entryPoint), _signer) {}
9+
}

0 commit comments

Comments
 (0)