Skip to content

Commit

Permalink
chore: remove permission hooks, add new internal helper function for …
Browse files Browse the repository at this point in the history
…devX, fix lint
  • Loading branch information
howydev committed Jun 28, 2024
1 parent 2ed6944 commit 49a8805
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 53 deletions.
24 changes: 24 additions & 0 deletions src/plugins/BasePlugin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
pragma solidity ^0.8.25;

import {ERC165, IERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import {IAccountExecute} from "@eth-infinitism/account-abstraction/interfaces/IAccountExecute.sol";
import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol";

import {IPlugin} from "../interfaces/IPlugin.sol";

Expand All @@ -27,4 +29,26 @@ abstract contract BasePlugin is ERC165, IPlugin {
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return interfaceId == type(IPlugin).interfaceId || super.supportsInterface(interfaceId);
}

function _getSelectorAndCalldata(bytes calldata data) internal view returns (bytes4, bytes memory) {
if (bytes4(data[:4]) == IAccountExecute.executeUserOp.selector) {
(PackedUserOperation memory uo,) = abi.decode(data[4:], (PackedUserOperation, bytes32));
bytes4 selector;
bytes memory callData = uo.callData;
// Bytes arr representation: [bytes32(len), bytes4(executeUserOp.selector), bytes4(actualSelector),
// bytes(actualCallData)]
// 1. Copy actualSelector into a new var
// 2. Shorten bytes arry by 8 by: store length - 8 into the new pointer location
// 3. Move the callData pointer by 8
assembly {
selector := mload(add(callData, 36))

let len := mload(callData)
mstore(add(callData, 8), sub(len, 8))
callData := add(callData, 8)
}
return (selector, callData);
}
return (bytes4(data[:4]), data[4:]);
}
}
99 changes: 46 additions & 53 deletions src/plugins/NativeTokenLimitPlugin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {PluginManifest, PluginMetadata} from "../interfaces/IPlugin.sol";
import {IStandardExecutor, Call} from "../interfaces/IStandardExecutor.sol";
import {IPlugin} from "../interfaces/IPlugin.sol";
import {IExecutionHook} from "../interfaces/IExecutionHook.sol";
import {IPermissionHook} from "../interfaces/IPermissionHook.sol";
import {IValidationHook} from "../interfaces/IValidationHook.sol";
import {BasePlugin, IERC165} from "./BasePlugin.sol";

Expand All @@ -18,7 +17,8 @@ import {BasePlugin, IERC165} from "./BasePlugin.sol";
/// @notice This plugin supports a single total native token spend limit.
/// It tracks a total spend limit across UserOperation gas limits and native token transfers.
/// If a paymaster is used, UO gas would not cause the limit to decrease.
contract NativeTokenLimitPlugin is BasePlugin, IExecutionHook, IPermissionHook, IValidationHook {

contract NativeTokenLimitPlugin is BasePlugin, IExecutionHook, IValidationHook {
using UserOperationLib for PackedUserOperation;
using EnumerableSet for EnumerableSet.Bytes32Set;

Expand All @@ -35,57 +35,6 @@ contract NativeTokenLimitPlugin is BasePlugin, IExecutionHook, IPermissionHook,
limits[msg.sender][functionId] = newLimit;
}

/// @inheritdoc IExecutionHook
function preExecutionHook(uint8 functionId, address, uint256, bytes calldata data)
external
override
returns (bytes memory)
{
return _checkAndDecrementLimit(functionId, data);
}

function preUserOpExecutionHook(uint8 functionId, PackedUserOperation calldata uo)
external
override
returns (bytes memory)
{
return _checkAndDecrementLimit(functionId, uo.callData);
}

function _checkAndDecrementLimit(uint8 functionId, bytes calldata data) internal returns (bytes memory) {
bytes4 execSelector = bytes4(data);
uint256 value;
// Get value being sent
if (execSelector == IStandardExecutor.execute.selector) {
value = uint256(bytes32(data[36:68]));
} else if (execSelector == IStandardExecutor.executeBatch.selector) {
Call[] memory calls = abi.decode(data[4:], (Call[]));
for (uint256 i = 0; i < calls.length; i++) {
value += calls[i].value;
}
}

uint256 limit = limits[msg.sender][functionId];
if (value > limit) {
revert ExceededNativeTokenLimit();
}
limits[msg.sender][functionId] = limit - value;

return "";
}

/// @inheritdoc IExecutionHook
function postExecutionHook(uint8, bytes calldata) external pure override {
revert NotImplemented();
}

// No implementation, no revert
// Runtime spends no account gas, and we check native token spend limits in exec hooks
function preRuntimeValidationHook(uint8 functionId, address, uint256, bytes calldata) external pure override {
// silence warnings
(functionId);
}

/// @inheritdoc IValidationHook
function preUserOpValidationHook(uint8 functionId, PackedUserOperation calldata userOp, bytes32)
external
Expand All @@ -107,6 +56,15 @@ contract NativeTokenLimitPlugin is BasePlugin, IExecutionHook, IPermissionHook,
return 0;
}

/// @inheritdoc IExecutionHook
function preExecutionHook(uint8 functionId, address, uint256, bytes calldata data)
external
override
returns (bytes memory)
{
return _checkAndDecrementLimit(functionId, data);
}

/// @inheritdoc IPlugin
function onInstall(bytes calldata data) external override {
uint256[] memory spendLimits = abi.decode(data, (uint256[]));
Expand All @@ -126,6 +84,18 @@ contract NativeTokenLimitPlugin is BasePlugin, IExecutionHook, IPermissionHook,
delete limits[msg.sender][functionId];
}

/// @inheritdoc IExecutionHook
function postExecutionHook(uint8, bytes calldata) external pure override {
revert NotImplemented();
}

// No implementation, no revert
// Runtime spends no account gas, and we check native token spend limits in exec hooks
function preRuntimeValidationHook(uint8 functionId, address, uint256, bytes calldata) external pure override {
// silence warnings
(functionId);
}

/// @inheritdoc IPlugin
function pluginManifest() external pure override returns (PluginManifest memory) {
// silence warnings
Expand Down Expand Up @@ -154,4 +124,27 @@ contract NativeTokenLimitPlugin is BasePlugin, IExecutionHook, IPermissionHook,
function supportsInterface(bytes4 interfaceId) public view override(BasePlugin, IERC165) returns (bool) {
return super.supportsInterface(interfaceId);
}

function _checkAndDecrementLimit(uint8 functionId, bytes calldata data) internal returns (bytes memory) {
(bytes4 selector, bytes memory callData) = _getSelectorAndCalldata(data);

uint256 value;
// Get value being sent
if (selector == IStandardExecutor.execute.selector) {
(, value) = abi.decode(callData, (address, uint256));
} else if (selector == IStandardExecutor.executeBatch.selector) {
Call[] memory calls = abi.decode(callData, (Call[]));
for (uint256 i = 0; i < calls.length; i++) {
value += calls[i].value;
}
}

uint256 limit = limits[msg.sender][functionId];
if (value > limit) {
revert ExceededNativeTokenLimit();
}
limits[msg.sender][functionId] = limit - value;

return "";
}
}

0 comments on commit 49a8805

Please sign in to comment.