Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: update to 4337 v0.7 (2/n) #49

Merged
merged 10 commits into from
May 1, 2024
1 change: 0 additions & 1 deletion src/account/AccountLoupe.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ abstract contract AccountLoupe is IAccountLoupe {

if (
selector == IStandardExecutor.execute.selector || selector == IStandardExecutor.executeBatch.selector
|| selector == UUPSUpgradeable.upgradeTo.selector
|| selector == UUPSUpgradeable.upgradeToAndCall.selector
|| selector == IPluginManager.installPlugin.selector
|| selector == IPluginManager.uninstallPlugin.selector
Expand Down
72 changes: 47 additions & 25 deletions src/account/AccountStorageInitializable.sol
Original file line number Diff line number Diff line change
@@ -1,45 +1,67 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.25;

import {Address} from "@openzeppelin/contracts/utils/Address.sol";

import {AccountStorage, getAccountStorage} from "./AccountStorage.sol";

/// @title AccountStorageInitializable
/// @dev Bulk of the impl is lifted from OZ 5.0 Initializible
abstract contract AccountStorageInitializable {
error AlreadyInitialized();
error AlreadyInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);

/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();

/**
* @dev The contract is not initializing.
*/
error NotInitializing();

/// @notice Modifier to put on function intended to be called only once per implementation
/// @dev Reverts if the contract has already been initialized
modifier initializer() {
AccountStorage storage _storage = getAccountStorage();
bool isTopLevelCall = !_storage.initializing;
if (
isTopLevelCall && _storage.initialized < 1
|| !Address.isContract(address(this)) && _storage.initialized == 1
) {
_storage.initialized = 1;
if (isTopLevelCall) {
_storage.initializing = true;
}
_;
if (isTopLevelCall) {
_storage.initializing = false;
}
} else {
revert AlreadyInitialized();
AccountStorage storage $ = getAccountStorage();

// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$.initializing;
uint64 initialized = $.initialized;

// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;

if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$.initialized = 1;
if (isTopLevelCall) {
$.initializing = true;
}
_;
if (isTopLevelCall) {
$.initializing = false;
emit Initialized(1);
}
}

/// @notice Internal function to disable calls to initialization functions
/// @dev Reverts if the contract has already been initialized
function _disableInitializers() internal virtual {
AccountStorage storage _storage = getAccountStorage();
if (_storage.initializing) {
revert AlreadyInitializing();
AccountStorage storage $ = getAccountStorage();
if ($.initializing) {
revert InvalidInitialization();
}
if (_storage.initialized != type(uint8).max) {
_storage.initialized = type(uint8).max;
if ($.initialized != type(uint8).max) {
$.initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
}
7 changes: 1 addition & 6 deletions src/account/UpgradeableModularAccount.sol
Original file line number Diff line number Diff line change
Expand Up @@ -292,11 +292,6 @@ contract UpgradeableModularAccount is
return getAccountStorage().supportedIfaces[interfaceId] > 0;
}

/// @inheritdoc UUPSUpgradeable
function upgradeTo(address newImplementation) public override onlyProxy wrapNativeFunction {
_upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
}

/// @inheritdoc UUPSUpgradeable
function upgradeToAndCall(address newImplementation, bytes memory data)
public
Expand All @@ -305,7 +300,7 @@ contract UpgradeableModularAccount is
onlyProxy
wrapNativeFunction
{
_upgradeToAndCallUUPS(newImplementation, data, true);
super.upgradeToAndCall(newImplementation, data);
}

/// @notice Gets the entry point for this account
Expand Down
34 changes: 10 additions & 24 deletions src/plugins/TokenReceiverPlugin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
pragma solidity ^0.8.25;

import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {IERC777Recipient} from "@openzeppelin/contracts/interfaces/IERC777Recipient.sol";
import {IERC1155Receiver} from "@openzeppelin/contracts/interfaces/IERC1155Receiver.sol";

import {
Expand All @@ -18,7 +17,7 @@ import {BasePlugin} from "./BasePlugin.sol";
/// @author ERC-6900 Authors
/// @notice This plugin allows modular accounts to receive various types of tokens by implementing
/// required token receiver interfaces.
contract TokenReceiverPlugin is BasePlugin, IERC721Receiver, IERC777Recipient, IERC1155Receiver {
contract TokenReceiverPlugin is BasePlugin, IERC721Receiver, IERC1155Receiver {
string public constant NAME = "Token Receiver Plugin";
string public constant VERSION = "1.0.0";
string public constant AUTHOR = "ERC-6900 Authors";
Expand All @@ -27,13 +26,6 @@ contract TokenReceiverPlugin is BasePlugin, IERC721Receiver, IERC777Recipient, I
// ┃ Execution functions ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

function tokensReceived(address, address, address, uint256, bytes calldata, bytes calldata)
external
pure
override
// solhint-disable-next-line no-empty-blocks
{}

function onERC721Received(address, address, uint256, bytes calldata) external pure override returns (bytes4) {
return IERC721Receiver.onERC721Received.selector;
}
Expand Down Expand Up @@ -72,40 +64,34 @@ contract TokenReceiverPlugin is BasePlugin, IERC721Receiver, IERC777Recipient, I
function pluginManifest() external pure override returns (PluginManifest memory) {
PluginManifest memory manifest;

manifest.executionFunctions = new bytes4[](4);
manifest.executionFunctions[0] = this.tokensReceived.selector;
manifest.executionFunctions[1] = this.onERC721Received.selector;
manifest.executionFunctions[2] = this.onERC1155Received.selector;
manifest.executionFunctions[3] = this.onERC1155BatchReceived.selector;
manifest.executionFunctions = new bytes4[](3);
manifest.executionFunctions[0] = this.onERC721Received.selector;
manifest.executionFunctions[1] = this.onERC1155Received.selector;
manifest.executionFunctions[2] = this.onERC1155BatchReceived.selector;

// Only runtime validationFunction is needed since callbacks come from token contracts only
ManifestFunction memory alwaysAllowRuntime = ManifestFunction({
functionType: ManifestAssociatedFunctionType.RUNTIME_VALIDATION_ALWAYS_ALLOW,
functionId: 0, // Unused.
dependencyIndex: 0 // Unused.
});
manifest.validationFunctions = new ManifestAssociatedFunction[](4);
manifest.validationFunctions = new ManifestAssociatedFunction[](3);
manifest.validationFunctions[0] = ManifestAssociatedFunction({
executionSelector: this.tokensReceived.selector,
associatedFunction: alwaysAllowRuntime
});
manifest.validationFunctions[1] = ManifestAssociatedFunction({
executionSelector: this.onERC721Received.selector,
associatedFunction: alwaysAllowRuntime
});
manifest.validationFunctions[2] = ManifestAssociatedFunction({
manifest.validationFunctions[1] = ManifestAssociatedFunction({
executionSelector: this.onERC1155Received.selector,
associatedFunction: alwaysAllowRuntime
});
manifest.validationFunctions[3] = ManifestAssociatedFunction({
manifest.validationFunctions[2] = ManifestAssociatedFunction({
executionSelector: this.onERC1155BatchReceived.selector,
associatedFunction: alwaysAllowRuntime
});

manifest.interfaceIds = new bytes4[](3);
manifest.interfaceIds = new bytes4[](2);
manifest.interfaceIds[0] = type(IERC721Receiver).interfaceId;
manifest.interfaceIds[1] = type(IERC777Recipient).interfaceId;
manifest.interfaceIds[2] = type(IERC1155Receiver).interfaceId;
manifest.interfaceIds[1] = type(IERC1155Receiver).interfaceId;

return manifest;
}
Expand Down
12 changes: 5 additions & 7 deletions src/plugins/owner/SingleOwnerPlugin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol";
import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";

import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
import {IPluginManager} from "../../interfaces/IPluginManager.sol";
Expand Down Expand Up @@ -38,6 +39,7 @@ import {ISingleOwnerPlugin} from "./ISingleOwnerPlugin.sol";
/// send user operations through a bundler.
contract SingleOwnerPlugin is BasePlugin, ISingleOwnerPlugin, IERC1271 {
using ECDSA for bytes32;
using MessageHashUtils for bytes32;

string public constant NAME = "Single Owner Plugin";
string public constant VERSION = "1.0.0";
Expand Down Expand Up @@ -99,7 +101,7 @@ contract SingleOwnerPlugin is BasePlugin, ISingleOwnerPlugin, IERC1271 {
{
if (functionId == uint8(FunctionId.VALIDATION_OWNER_OR_SELF)) {
// Validate the user op signature against the owner.
(address signer,) = (userOpHash.toEthSignedMessageHash()).tryRecover(userOp.signature);
(address signer,,) = (userOpHash.toEthSignedMessageHash()).tryRecover(userOp.signature);
if (signer == address(0) || signer != _owners[msg.sender]) {
return _SIG_VALIDATION_FAILED;
}
Expand Down Expand Up @@ -154,7 +156,7 @@ contract SingleOwnerPlugin is BasePlugin, ISingleOwnerPlugin, IERC1271 {
functionId: uint8(FunctionId.VALIDATION_OWNER_OR_SELF),
dependencyIndex: 0 // Unused.
});
manifest.validationFunctions = new ManifestAssociatedFunction[](8);
manifest.validationFunctions = new ManifestAssociatedFunction[](7);
manifest.validationFunctions[0] = ManifestAssociatedFunction({
executionSelector: this.transferOwnership.selector,
associatedFunction: ownerValidationFunction
Expand All @@ -176,10 +178,6 @@ contract SingleOwnerPlugin is BasePlugin, ISingleOwnerPlugin, IERC1271 {
associatedFunction: ownerValidationFunction
});
manifest.validationFunctions[5] = ManifestAssociatedFunction({
executionSelector: UUPSUpgradeable.upgradeTo.selector,
associatedFunction: ownerValidationFunction
});
manifest.validationFunctions[6] = ManifestAssociatedFunction({
executionSelector: UUPSUpgradeable.upgradeToAndCall.selector,
associatedFunction: ownerValidationFunction
});
Expand All @@ -189,7 +187,7 @@ contract SingleOwnerPlugin is BasePlugin, ISingleOwnerPlugin, IERC1271 {
functionId: 0, // Unused.
dependencyIndex: 0 // Unused.
});
manifest.validationFunctions[7] = ManifestAssociatedFunction({
manifest.validationFunctions[6] = ManifestAssociatedFunction({
executionSelector: this.isValidSignature.selector,
associatedFunction: alwaysAllowRuntime
});
Expand Down
16 changes: 16 additions & 0 deletions test/account/UpgradeableModularAccount.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {console} from "forge-std/Test.sol";

import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";

import {PluginManagerInternals} from "../../src/account/PluginManagerInternals.sol";
import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol";
Expand All @@ -23,6 +24,7 @@ import {AccountTestBase} from "../utils/AccountTestBase.sol";

contract UpgradeableModularAccountTest is AccountTestBase {
using ECDSA for bytes32;
using MessageHashUtils for bytes32;

TokenReceiverPlugin public tokenReceiverPlugin;

Expand Down Expand Up @@ -403,6 +405,20 @@ contract UpgradeableModularAccountTest is AccountTestBase {
vm.stopPrank();
}

function test_upgradeToAndCall() public {
vm.startPrank(owner1);
UpgradeableModularAccount account3 = new UpgradeableModularAccount(entryPoint);
bytes32 slot = account3.proxiableUUID();

// account has impl from factory
assertEq(
address(factory.accountImplementation()), address(uint160(uint256(vm.load(address(account1), slot))))
);
account1.upgradeToAndCall(address(account3), bytes(""));
// account has new impl
assertEq(address(account3), address(uint160(uint256(vm.load(address(account1), slot)))));
}

// Internal Functions

function _printStorageReadsAndWrites(address addr) internal {
Expand Down
4 changes: 3 additions & 1 deletion test/comparison/CompareSimpleAccount.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity ^0.8.19;
import {Test} from "forge-std/Test.sol";

import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol";
import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol";

Expand All @@ -14,6 +15,7 @@ import {Counter} from "../mocks/Counter.sol";

contract CompareSimpleAccountTest is Test {
using ECDSA for bytes32;
using MessageHashUtils for bytes32;

EntryPoint public entryPoint;
address payable public beneficiary;
Expand All @@ -37,7 +39,7 @@ contract CompareSimpleAccountTest is Test {

// helper function to compress 2 gas values into a single bytes32
function _encodeGas(uint256 g1, uint256 g2) internal pure returns (bytes32) {
return bytes32(uint256(g1 << 128 + uint128(g2)));
return bytes32(uint256((g1 << 128) + uint128(g2)));
}

function setUp() public {
Expand Down
2 changes: 1 addition & 1 deletion test/libraries/AccountStorage.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ contract AccountStorageTest is Test {
assertEq(uint256(vm.load(address(impl), _ACCOUNT_STORAGE_SLOT)), type(uint8).max);

// should revert if we try to initialize again
vm.expectRevert(AccountStorageInitializable.AlreadyInitialized.selector);
vm.expectRevert(AccountStorageInitializable.InvalidInitialization.selector);
impl.initialize();
}

Expand Down
12 changes: 12 additions & 0 deletions test/mocks/MockERC721.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;

import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract MockERC721 is ERC721 {
constructor(string memory name, string memory symbol) ERC721(name, symbol) {}

function mint(address account, uint256 tokenId) external {
_mint(account, tokenId);
}
}
51 changes: 0 additions & 51 deletions test/mocks/MockERC777.sol

This file was deleted.

Loading
Loading