Skip to content

Commit

Permalink
Add userop validator handler
Browse files Browse the repository at this point in the history
  • Loading branch information
akshay-ap committed Oct 27, 2023
1 parent a603214 commit 5b5be8f
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 13 deletions.
2 changes: 1 addition & 1 deletion contracts/SafeProtocolManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {MODULE_TYPE_PLUGIN} from "./common/Constants.sol";
* plugins through a Manager rather than directly enabling plugins in their Account.
* Users have to first enable SafeProtocolManager as a plugin on their Account and then enable other plugins through the manager.
*/
contract SafeProtocolManager is ISafeProtocolManager, RegistryManager, HooksManager, FunctionHandlerManager, IERC165 {
contract SafeProtocolManager is ISafeProtocolManager, RegistryManager, HooksManager, IERC165 {
address internal constant SENTINEL_MODULES = address(0x1);

/**
Expand Down
34 changes: 27 additions & 7 deletions contracts/base/FunctionHandlerManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,20 @@ import {MODULE_TYPE_FUNCTION_HANDLER} from "../common/Constants.sol";
* @notice This contract manages the function handlers for an Account. The contract stores the
* information about an account, bytes4 function selector and the function handler contract address.
*/
abstract contract FunctionHandlerManager is RegistryManager {
contract FunctionHandlerManager is RegistryManager {
// Storage
/** @dev Mapping that stores information about an account, function selector, and address of the account.
*/
mapping(address => mapping(bytes4 => address)) public functionHandlers;

mapping(address => bool) public isValidateUserOpHandlerEnabled;
address public validateUserOpHandler;
bytes4 constant validateUserOpSelector = bytes4(0x3a871cdd);

Check warning on line 23 in contracts/base/FunctionHandlerManager.sol

View workflow job for this annotation

GitHub Actions / lint

Explicitly mark visibility of state

Check warning on line 23 in contracts/base/FunctionHandlerManager.sol

View workflow job for this annotation

GitHub Actions / lint

Constant name must be in capitalized SNAKE_CASE

constructor(address _validateUserOpHandler, address registry, address initialOwner) RegistryManager(registry, initialOwner) {
validateUserOpHandler = _validateUserOpHandler;
}

// Events
event FunctionHandlerChanged(address indexed account, bytes4 indexed selector, address indexed functionHandler);

Expand Down Expand Up @@ -52,6 +60,14 @@ abstract contract FunctionHandlerManager is RegistryManager {
emit FunctionHandlerChanged(msg.sender, selector, functionHandler);
}

function setValidateUserOpHandler(address newHandler) external onlyOwner {
validateUserOpHandler = newHandler;
}

function enableUserOpValidator() external onlyAccount {
isValidateUserOpHandlerEnabled[msg.sender] = true;
}

/**
* @notice This fallback handler function checks if an account (msg.sender) has a function handler enabled.
* If enabled, calls handle function and returns the result back.
Expand All @@ -63,19 +79,23 @@ abstract contract FunctionHandlerManager is RegistryManager {
address account = msg.sender;
bytes4 functionSelector = bytes4(msg.data);

address sender;
// solhint-disable-next-line no-inline-assembly
assembly {
sender := shr(96, calldataload(sub(calldatasize(), 20)))
}

if (validateUserOpSelector == functionSelector && isValidateUserOpHandlerEnabled[account]) {
return ISafeProtocolFunctionHandler(validateUserOpHandler).handle(account, sender, 0, msg.data);
}

address functionHandler = functionHandlers[account][functionSelector];

// Revert if functionHandler is not set
if (functionHandler == address(0)) {
revert FunctionHandlerNotSet(account, functionSelector);
}

address sender;
// solhint-disable-next-line no-inline-assembly
assembly {
sender := shr(96, calldataload(sub(calldatasize(), 20)))
}

// With a Safe{Core} Account v1.x, msg.data contains 20 bytes of sender address. Read the sender address by loading last 20 bytes.
// remove last 20 bytes from calldata and store it in `data`.
// Keep first 4 bytes (i.e function signature) so that handler contract can infer function identifier.
Expand Down
10 changes: 9 additions & 1 deletion deploy/deploy_protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { HardhatRuntimeEnvironment } from "hardhat/types";

const deploy: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { deployments, getNamedAccounts } = hre;
const { deployer, owner } = await getNamedAccounts();
const { deployer, owner, userOpValidatorHandler } = await getNamedAccounts();
const { deploy } = deployments;
const registry = await deploy("SafeProtocolRegistry", {
from: deployer,
Expand All @@ -18,6 +18,14 @@ const deploy: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
log: true,
deterministicDeployment: true,
});


await deploy("FunctionHandlerManager", {
from: deployer,
args: [userOpValidatorHandler, registry.address, owner],
log: true,
deterministicDeployment: true,
});
};

deploy.tags = ["protocol"];
Expand Down
9 changes: 8 additions & 1 deletion deploy/deploy_protocol_with_test_registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { HardhatRuntimeEnvironment } from "hardhat/types";

const deploy: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { deployments, getNamedAccounts } = hre;
const { deployer, owner } = await getNamedAccounts();
const { deployer, owner, userOpValidatorHandler } = await getNamedAccounts();
const { deploy } = deployments;
const testRegistry = await deploy("TestSafeProtocolRegistryUnrestricted", {
from: deployer,
Expand All @@ -18,6 +18,13 @@ const deploy: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
log: true,
deterministicDeployment: true,
});

await deploy("FunctionHandlerManager", {
from: deployer,
args: [userOpValidatorHandler, testRegistry.address, owner],
log: true,
deterministicDeployment: true,
});
};

deploy.tags = ["test-protocol"];
Expand Down
18 changes: 15 additions & 3 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { HttpNetworkUserConfig } from "hardhat/types";
import "hardhat-deploy";
import { DeterministicDeploymentInfo } from "hardhat-deploy/dist/types";
import { getSingletonFactoryInfo } from "@safe-global/safe-singleton-factory";
import { ethers } from "ethers";
import { ZeroAddress, ethers } from "ethers";
import "./src/tasks/generate_deployments_markdown";
import "./src/tasks/show_codesize";

Expand All @@ -22,7 +22,7 @@ const argv : any = yargs
.help(false)
.version(false).argv;

const { NODE_URL, MNEMONIC, INFURA_KEY, ETHERSCAN_API_KEY, SAFE_CORE_PROTOCOL_OWNER_ADDRESS } = process.env;
const { NODE_URL, MNEMONIC, INFURA_KEY, ETHERSCAN_API_KEY, SAFE_CORE_PROTOCOL_OWNER_ADDRESS, SAFE_CORE_PROTOCOL_4337_USER_OP_VALIDATOR_HANDLER_ADDRESS } = process.env;

const deterministicDeployment = (network: string): DeterministicDeploymentInfo => {
const info = getSingletonFactoryInfo(parseInt(network));
Expand Down Expand Up @@ -52,10 +52,19 @@ sharedNetworkConfig.accounts = {
}

const config: HardhatUserConfig = {
solidity: "0.8.18",
solidity: {
version: "0.8.18",
settings: {
optimizer: {
enabled: true,
runs: 2000
},
}
},
gasReporter: {
enabled: (process.env.REPORT_GAS) ? true : false
},

networks: {
hardhat: {
allowUnlimitedContractSize: true,
Expand Down Expand Up @@ -109,6 +118,9 @@ const config: HardhatUserConfig = {
},
owner: {
default: SAFE_CORE_PROTOCOL_OWNER_ADDRESS || 1
},
userOpValidatorHandler: {
default: SAFE_CORE_PROTOCOL_4337_USER_OP_VALIDATOR_HANDLER_ADDRESS || ZeroAddress
}
}
};
Expand Down

0 comments on commit 5b5be8f

Please sign in to comment.