• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

safe-global / safe-core-protocol / 6800083263

08 Nov 2023 03:03PM UTC coverage: 97.653% (-2.3%) from 100.0%
6800083263

push

github

nlordell
Added Sample 4337 Module

157 of 164 branches covered (0.0%)

Branch coverage included in aggregate %.

32 of 35 new or added lines in 1 file covered. (91.43%)

259 of 262 relevant lines covered (98.85%)

16.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.13
/contracts/modules/erc4337/SafeProtocol4337Module.sol
1
// SPDX-License-Identifier: LGPL-3.0-only
2
pragma solidity ^0.8.18;
3

4
import {SafeProtocolAction, SafeTransaction} from "../../DataTypes.sol";
5
import {PLUGIN_PERMISSION_EXECUTE_CALL} from "../../common/Constants.sol";
6
import {IAccount} from "../../interfaces/Accounts.sol";
7
import {ISafeProtocolManager} from "../../interfaces/Manager.sol";
8
import {IERC165, ISafeProtocolFunctionHandler, ISafeProtocolPlugin} from "../../interfaces/Modules.sol";
9
import {UserOperation} from "./interfaces/IERC4337.sol";
10
import {ISafeProtocol4337Handler} from "./interfaces/ISafeProtocol4337Handler.sol";
11

12
contract SafeProtocol4337Module is ISafeProtocolFunctionHandler, ISafeProtocolPlugin {
13
    uint256 private constant VALIDATION_SIG_SUCCESS = 0;
14
    uint256 private constant VALIDATION_SIG_FAILURE = 0;
15

16
    address payable public immutable entrypoint;
17

18
    constructor(address payable _entrypoint) {
19
        require(_entrypoint != address(0), "invalid entrypoint address");
1!
20
        entrypoint = _entrypoint;
1✔
21
    }
22

23
    /**
24
     * @inheritdoc IERC165
25
     */
26
    function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
27
        return
5✔
28
            interfaceId == type(IERC165).interfaceId ||
8!
29
            interfaceId == type(ISafeProtocolFunctionHandler).interfaceId ||
30
            interfaceId == type(ISafeProtocolPlugin).interfaceId;
31
    }
32

33
    /**
34
     * @inheritdoc ISafeProtocolFunctionHandler
35
     */
36
    function handle(address account, address sender, uint256 value, bytes calldata data) external override returns (bytes memory result) {
37
        require(sender == entrypoint, "unsupported entrypoint");
2!
38
        require(value == 0, "not payable");
2!
39

40
        ISafeProtocolManager manager = ISafeProtocolManager(msg.sender);
2✔
41
        bytes4 selector = bytes4(data[:4]);
2✔
42
        if (selector == ISafeProtocol4337Handler(account).validateUserOp.selector) {
2✔
43
            (UserOperation memory userOp, bytes32 userOpHash, uint256 missingAccountFunds) = abi.decode(
1✔
44
                data[4:],
45
                (UserOperation, bytes32, uint256)
46
            );
47
            uint256 validationData = _validateUserOp(manager, account, userOp, userOpHash, missingAccountFunds);
1✔
48
            result = abi.encode(validationData);
1✔
49
        } else if (selector == ISafeProtocol4337Handler(account).executeUserOp.selector) {
1!
50
            (address to, uint256 opValue, bytes memory opData) = abi.decode(data[4:], (address, uint256, bytes));
1✔
51
            _executeUserOp(manager, account, to, opValue, opData);
1✔
52
        }
53
    }
54

55
    /**
56
     * Validate account operation.
57
     * @param manager the protocol manager.
58
     * @param account the account.
59
     * @param userOp the operation that is about to be executed.
60
     * @param missingAccountFunds missing funds on the account's deposit in the entrypoint.
61
     * @return validationData packaged validation data.
62
     */
63
    function _validateUserOp(
64
        ISafeProtocolManager manager,
65
        address account,
66
        UserOperation memory userOp,
67
        bytes32 userOpHash,
68
        uint256 missingAccountFunds
69
    ) internal returns (uint256 validationData) {
70
        require(bytes4(userOp.callData) == ISafeProtocol4337Handler(account).executeUserOp.selector, "unsupported execution");
1!
71

72
        if (missingAccountFunds > 0) {
1!
73
            SafeTransaction memory transaction;
1✔
74
            {
1✔
75
                transaction.actions = new SafeProtocolAction[](1);
1✔
76
                transaction.actions[0].to = entrypoint;
1✔
77
                transaction.actions[0].value = missingAccountFunds;
1✔
78
                transaction.nonce = userOp.nonce;
1✔
79
            }
80
            manager.executeTransaction(account, transaction);
1✔
81
        }
82

83
        try IAccount(account).checkSignatures(userOpHash, "", userOp.signature) {
1✔
84
            validationData = VALIDATION_SIG_SUCCESS;
1✔
85
        } catch {
NEW
86
            validationData = VALIDATION_SIG_FAILURE;
×
87
        }
88
    }
89

90
    /**
91
     * Executes a account operation.
92
     * @param manager the protocol manager.
93
     * @param account the account.
94
     * @param to target of the operation.
95
     * @param value value of the operation.
96
     * @param data calldata for the operation.
97
     */
98
    function _executeUserOp(ISafeProtocolManager manager, address account, address to, uint256 value, bytes memory data) internal {
99
        SafeTransaction memory transaction;
1✔
100
        {
1✔
101
            transaction.actions = new SafeProtocolAction[](1);
1✔
102
            transaction.actions[0].to = payable(to);
1✔
103
            transaction.actions[0].value = value;
1✔
104
            transaction.actions[0].data = data;
1✔
105
        }
106
        manager.executeTransaction(account, transaction);
1✔
107
    }
108

109
    /**
110
     * @inheritdoc ISafeProtocolFunctionHandler
111
     */
112
    function metadataProvider()
113
        external
114
        view
115
        override(ISafeProtocolFunctionHandler, ISafeProtocolPlugin)
116
        returns (uint256 providerType, bytes memory location)
117
    {}
118

119
    /**
120
     * @notice A funtion that returns name of the plugin
121
     * @return name string name of the plugin
122
     */
123
    function name() external pure override returns (string memory) {
NEW
124
        return "Safe Protocol ERC-4337 Plugin";
×
125
    }
126

127
    /**
128
     * @notice A function that returns version of the plugin
129
     * @return version string version of the plugin
130
     */
131
    function version() external pure override returns (string memory) {
NEW
132
        return "1";
×
133
    }
134

135
    /**
136
     * @notice A function that indicates permissions required by the.
137
     * @dev Permissions types and value: EXECUTE_CALL = 1, CALL_TO_SELF = 2, EXECUTE_DELEGATECALL = 4.
138
     *      These values can be sumed to indicate multiple permissions. e.g. EXECUTE_CALL + CALL_TO_SELF = 3
139
     * @return permissions Bit-based permissions required by the plugin.
140
     */
141
    function requiresPermissions() external pure override returns (uint8 permissions) {
142
        permissions = PLUGIN_PERMISSION_EXECUTE_CALL;
3✔
143
    }
144
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc