diff --git a/src/account/AccountLoupe.sol b/src/account/AccountLoupe.sol index 8bff326c..a75def37 100644 --- a/src/account/AccountLoupe.sol +++ b/src/account/AccountLoupe.sol @@ -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 diff --git a/src/account/AccountStorageInitializable.sol b/src/account/AccountStorageInitializable.sol index 5131978b..debcee40 100644 --- a/src/account/AccountStorageInitializable.sol +++ b/src/account/AccountStorageInitializable.sol @@ -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); } } } diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index 81496a2a..f29d17c7 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -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 @@ -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 diff --git a/src/plugins/TokenReceiverPlugin.sol b/src/plugins/TokenReceiverPlugin.sol index f22ae3ba..980d7397 100644 --- a/src/plugins/TokenReceiverPlugin.sol +++ b/src/plugins/TokenReceiverPlugin.sol @@ -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 { @@ -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"; @@ -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; } @@ -72,11 +64,10 @@ 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({ @@ -84,28 +75,23 @@ contract TokenReceiverPlugin is BasePlugin, IERC721Receiver, IERC777Recipient, I 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; } diff --git a/src/plugins/owner/SingleOwnerPlugin.sol b/src/plugins/owner/SingleOwnerPlugin.sol index bfff2aa4..b1d5d5e1 100644 --- a/src/plugins/owner/SingleOwnerPlugin.sol +++ b/src/plugins/owner/SingleOwnerPlugin.sol @@ -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"; @@ -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"; @@ -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; } @@ -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 @@ -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 }); @@ -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 }); diff --git a/test/account/UpgradeableModularAccount.t.sol b/test/account/UpgradeableModularAccount.t.sol index ae491c13..3ef6f107 100644 --- a/test/account/UpgradeableModularAccount.t.sol +++ b/test/account/UpgradeableModularAccount.t.sol @@ -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"; @@ -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; @@ -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 { diff --git a/test/comparison/CompareSimpleAccount.t.sol b/test/comparison/CompareSimpleAccount.t.sol index 7c3182b2..93b75ebe 100644 --- a/test/comparison/CompareSimpleAccount.t.sol +++ b/test/comparison/CompareSimpleAccount.t.sol @@ -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"; @@ -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; @@ -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 { diff --git a/test/libraries/AccountStorage.t.sol b/test/libraries/AccountStorage.t.sol index 7cab9991..f59049a3 100644 --- a/test/libraries/AccountStorage.t.sol +++ b/test/libraries/AccountStorage.t.sol @@ -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(); } diff --git a/test/mocks/MockERC721.sol b/test/mocks/MockERC721.sol new file mode 100644 index 00000000..1139adf2 --- /dev/null +++ b/test/mocks/MockERC721.sol @@ -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); + } +} diff --git a/test/mocks/MockERC777.sol b/test/mocks/MockERC777.sol deleted file mode 100644 index 18840db2..00000000 --- a/test/mocks/MockERC777.sol +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; - -import {IERC777} from "@openzeppelin/contracts/token/ERC777/IERC777.sol"; -import {IERC777Recipient} from "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol"; - -contract MockERC777 is IERC777 { - string public override name; - string public override symbol; - uint256 public override granularity; - uint256 public override totalSupply; - mapping(address => uint256) public override balanceOf; - - function mint(address to, uint256 amount) public { - balanceOf[to] += amount; - } - - function transfer(address to, uint256 amount) public returns (bool) { - return transferFrom(msg.sender, to, amount); - } - - function transferFrom(address from, address to, uint256 amount) public returns (bool) { - IERC777Recipient(to).tokensReceived(msg.sender, from, to, amount, bytes(""), bytes("")); - balanceOf[from] -= amount; - balanceOf[to] += amount; - return true; - } - - function send(address to, uint256 amount, bytes calldata) public override { - transferFrom(msg.sender, to, amount); - } - - function burn(uint256 amount, bytes calldata) external { - transferFrom(msg.sender, address(0), amount); - } - - function isOperatorFor(address, address) external pure returns (bool) { - return false; - } - - // solhint-disable-next-line no-empty-blocks - function authorizeOperator(address) external {} - // solhint-disable-next-line no-empty-blocks - function revokeOperator(address) external {} - // solhint-disable-next-line no-empty-blocks - function defaultOperators() external view returns (address[] memory a) {} - // solhint-disable-next-line no-empty-blocks - function operatorSend(address, address, uint256, bytes calldata, bytes calldata) external {} - // solhint-disable-next-line no-empty-blocks - function operatorBurn(address, uint256, bytes calldata, bytes calldata) external {} -} diff --git a/test/plugin/SingleOwnerPlugin.t.sol b/test/plugin/SingleOwnerPlugin.t.sol index 73e45648..dea5b2f9 100644 --- a/test/plugin/SingleOwnerPlugin.t.sol +++ b/test/plugin/SingleOwnerPlugin.t.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.19; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; import {SingleOwnerPlugin} from "../../src/plugins/owner/SingleOwnerPlugin.sol"; import {ISingleOwnerPlugin} from "../../src/plugins/owner/ISingleOwnerPlugin.sol"; @@ -12,6 +13,7 @@ import {OptimizedTest} from "../utils/OptimizedTest.sol"; contract SingleOwnerPluginTest is OptimizedTest { using ECDSA for bytes32; + using MessageHashUtils for bytes32; SingleOwnerPlugin public plugin; EntryPoint public entryPoint; diff --git a/test/plugin/TokenReceiverPlugin.t.sol b/test/plugin/TokenReceiverPlugin.t.sol index 49692e34..7a7433af 100644 --- a/test/plugin/TokenReceiverPlugin.t.sol +++ b/test/plugin/TokenReceiverPlugin.t.sol @@ -3,9 +3,6 @@ pragma solidity ^0.8.19; import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol"; import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; -import {ERC721PresetMinterPauserAutoId} from - "@openzeppelin/contracts/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol"; -import {IERC777Recipient} from "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol"; import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; @@ -13,7 +10,7 @@ import {FunctionReference} from "../../src/helpers/FunctionReferenceLib.sol"; import {TokenReceiverPlugin} from "../../src/plugins/TokenReceiverPlugin.sol"; import {MSCAFactoryFixture} from "../mocks/MSCAFactoryFixture.sol"; -import {MockERC777} from "../mocks/MockERC777.sol"; +import {MockERC721} from "../mocks/MockERC721.sol"; import {MockERC1155} from "../mocks/MockERC1155.sol"; import {OptimizedTest} from "../utils/OptimizedTest.sol"; @@ -21,9 +18,8 @@ contract TokenReceiverPluginTest is OptimizedTest, IERC1155Receiver { UpgradeableModularAccount public acct; TokenReceiverPlugin public plugin; - ERC721PresetMinterPauserAutoId public t0; - MockERC777 public t1; - MockERC1155 public t2; + MockERC721 public t0; + MockERC1155 public t1; // init dynamic length arrays for use in args address[] public defaultOperators; @@ -41,16 +37,13 @@ contract TokenReceiverPluginTest is OptimizedTest, IERC1155Receiver { acct = factory.createAccount(address(this), 0); plugin = _deployTokenReceiverPlugin(); - t0 = new ERC721PresetMinterPauserAutoId("t0", "t0", ""); - t0.mint(address(this)); + t0 = new MockERC721("t0", "t0"); + t0.mint(address(this), _TOKEN_ID); - t1 = new MockERC777(); - t1.mint(address(this), _TOKEN_AMOUNT); - - t2 = new MockERC1155(); - t2.mint(address(this), _TOKEN_ID, _TOKEN_AMOUNT); + t1 = new MockERC1155(); + t1.mint(address(this), _TOKEN_ID, _TOKEN_AMOUNT); for (uint256 i = 1; i < _BATCH_TOKEN_IDS; i++) { - t2.mint(address(this), i, _TOKEN_AMOUNT); + t1.mint(address(this), i, _TOKEN_AMOUNT); tokenIds.push(i); tokenAmts.push(_TOKEN_AMOUNT); zeroTokenAmts.push(0); @@ -81,54 +74,45 @@ contract TokenReceiverPluginTest is OptimizedTest, IERC1155Receiver { assertEq(t0.ownerOf(_TOKEN_ID), address(acct)); } - function test_failERC777Transfer() public { + function test_failERC1155Transfer() public { + // for 1155, reverts are caught in a try catch and bubbled up with the reason from the account vm.expectRevert( abi.encodePacked( UpgradeableModularAccount.UnrecognizedFunction.selector, - IERC777Recipient.tokensReceived.selector, + IERC1155Receiver.onERC1155Received.selector, bytes28(0) ) ); - t1.transfer(address(acct), _TOKEN_AMOUNT); - } - - function test_passERC777Transfer() public { - _initPlugin(); - - assertEq(t1.balanceOf(address(this)), _TOKEN_AMOUNT); - assertEq(t1.balanceOf(address(acct)), 0); - t1.transfer(address(acct), _TOKEN_AMOUNT); - assertEq(t1.balanceOf(address(this)), 0); - assertEq(t1.balanceOf(address(acct)), _TOKEN_AMOUNT); - } - - function test_failERC1155Transfer() public { - // for 1155, reverts are caught in a try catch and bubbled up with a diff reason - vm.expectRevert("ERC1155: transfer to non-ERC1155Receiver implementer"); - t2.safeTransferFrom(address(this), address(acct), _TOKEN_ID, _TOKEN_AMOUNT, ""); + t1.safeTransferFrom(address(this), address(acct), _TOKEN_ID, _TOKEN_AMOUNT, ""); - // for 1155, reverts are caught in a try catch and bubbled up with a diff reason - vm.expectRevert("ERC1155: transfer to non-ERC1155Receiver implementer"); - t2.safeBatchTransferFrom(address(this), address(acct), tokenIds, tokenAmts, ""); + // for 1155, reverts are caught in a try catch and bubbled up with the reason from the account + vm.expectRevert( + abi.encodePacked( + UpgradeableModularAccount.UnrecognizedFunction.selector, + IERC1155Receiver.onERC1155BatchReceived.selector, + bytes28(0) + ) + ); + t1.safeBatchTransferFrom(address(this), address(acct), tokenIds, tokenAmts, ""); } function test_passERC1155Transfer() public { _initPlugin(); - assertEq(t2.balanceOf(address(this), _TOKEN_ID), _TOKEN_AMOUNT); - assertEq(t2.balanceOf(address(acct), _TOKEN_ID), 0); - t2.safeTransferFrom(address(this), address(acct), _TOKEN_ID, _TOKEN_AMOUNT, ""); - assertEq(t2.balanceOf(address(this), _TOKEN_ID), 0); - assertEq(t2.balanceOf(address(acct), _TOKEN_ID), _TOKEN_AMOUNT); + assertEq(t1.balanceOf(address(this), _TOKEN_ID), _TOKEN_AMOUNT); + assertEq(t1.balanceOf(address(acct), _TOKEN_ID), 0); + t1.safeTransferFrom(address(this), address(acct), _TOKEN_ID, _TOKEN_AMOUNT, ""); + assertEq(t1.balanceOf(address(this), _TOKEN_ID), 0); + assertEq(t1.balanceOf(address(acct), _TOKEN_ID), _TOKEN_AMOUNT); for (uint256 i = 1; i < _BATCH_TOKEN_IDS; i++) { - assertEq(t2.balanceOf(address(this), i), _TOKEN_AMOUNT); - assertEq(t2.balanceOf(address(acct), i), 0); + assertEq(t1.balanceOf(address(this), i), _TOKEN_AMOUNT); + assertEq(t1.balanceOf(address(acct), i), 0); } - t2.safeBatchTransferFrom(address(this), address(acct), tokenIds, tokenAmts, ""); + t1.safeBatchTransferFrom(address(this), address(acct), tokenIds, tokenAmts, ""); for (uint256 i = 1; i < _BATCH_TOKEN_IDS; i++) { - assertEq(t2.balanceOf(address(this), i), 0); - assertEq(t2.balanceOf(address(acct), i), _TOKEN_AMOUNT); + assertEq(t1.balanceOf(address(this), i), 0); + assertEq(t1.balanceOf(address(acct), i), _TOKEN_AMOUNT); } } @@ -137,8 +121,6 @@ contract TokenReceiverPluginTest is OptimizedTest, IERC1155Receiver { isSupported = acct.supportsInterface(type(IERC721Receiver).interfaceId); assertEq(isSupported, false); - isSupported = acct.supportsInterface(type(IERC777Recipient).interfaceId); - assertEq(isSupported, false); isSupported = acct.supportsInterface(type(IERC1155Receiver).interfaceId); assertEq(isSupported, false); } @@ -150,8 +132,6 @@ contract TokenReceiverPluginTest is OptimizedTest, IERC1155Receiver { isSupported = acct.supportsInterface(type(IERC721Receiver).interfaceId); assertEq(isSupported, true); - isSupported = acct.supportsInterface(type(IERC777Recipient).interfaceId); - assertEq(isSupported, true); isSupported = acct.supportsInterface(type(IERC1155Receiver).interfaceId); assertEq(isSupported, true); } diff --git a/test/utils/AccountTestBase.sol b/test/utils/AccountTestBase.sol index e23ed1ee..5d9880f9 100644 --- a/test/utils/AccountTestBase.sol +++ b/test/utils/AccountTestBase.sol @@ -42,6 +42,6 @@ abstract contract AccountTestBase is OptimizedTest { // 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))); } }