diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index 4599b315..208a7a13 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -28,7 +28,7 @@ import {UUPSUpgradeable} from "../../ext/UUPSUpgradeable.sol"; /// @title Upgradeable Modular Account /// @author Alchemy -/// @notice An ERC-6900 compatible modular smart contract account that supports upgradeability and plugins. +/// @notice An ERC-6900 compatible modular smart contract account (MSCA) that supports upgradeability and plugins. contract UpgradeableModularAccount is AccountExecutor, AccountLoupe, diff --git a/test/upgrade/LightAccountToMSCA.t.sol b/test/upgrade/LightAccountToMSCA.t.sol index f36a94f5..2e7b3ce7 100644 --- a/test/upgrade/LightAccountToMSCA.t.sol +++ b/test/upgrade/LightAccountToMSCA.t.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.21; import {Test} from "forge-std/Test.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {LightAccount} from "@alchemy/light-account/src/LightAccount.sol"; import {LightAccountFactory} from "@alchemy/light-account/src/LightAccountFactory.sol"; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; @@ -16,8 +15,6 @@ import {IEntryPoint as IMSCAEntryPoint} from "../../src/interfaces/erc4337/IEntr import {MockERC20} from "../mocks/tokens/MockERC20.sol"; contract LightAccountToMSCATest is Test { - using ECDSA for bytes32; - IEntryPoint public entryPoint; IMSCAEntryPoint public mscaEntryPoint; diff --git a/test/upgrade/MSCAToMSCA.t.sol b/test/upgrade/MSCAToMSCA.t.sol new file mode 100644 index 00000000..aa3cc51f --- /dev/null +++ b/test/upgrade/MSCAToMSCA.t.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.21; + +import {Test} from "forge-std/Test.sol"; + +import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; + +import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; +import {MultiOwnerTokenReceiverMSCAFactory} from "../../src/factory/MultiOwnerTokenReceiverMSCAFactory.sol"; +import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; +import {TokenReceiverPlugin} from "../../src/plugins/TokenReceiverPlugin.sol"; +import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; + +import {Utils} from "../Utils.sol"; +import {MockERC20} from "../mocks/tokens/MockERC20.sol"; + +contract MSCAToMSCATest is Test { + IEntryPoint public entryPoint; + + MockERC20 public token1; + + address[] public owners; + UpgradeableModularAccount public msca; + + MultiOwnerPlugin public multiOwnerPlugin; + TokenReceiverPlugin public tokenReceiverPlugin; + address public mscaImpl1; + address public mscaImpl2; + + event Upgraded(address indexed implementation); + + function setUp() public { + owners.push(makeAddr("owner1")); + owners.push(makeAddr("owner2")); + entryPoint = IEntryPoint(address(new EntryPoint())); + mscaImpl1 = address(new UpgradeableModularAccount(entryPoint)); + mscaImpl2 = address(new UpgradeableModularAccount(entryPoint)); + multiOwnerPlugin = new MultiOwnerPlugin(); + tokenReceiverPlugin = new TokenReceiverPlugin(); + bytes32 ownerManifestHash = keccak256(abi.encode(multiOwnerPlugin.pluginManifest())); + bytes32 tokenReceiverManifestHash = keccak256(abi.encode(tokenReceiverPlugin.pluginManifest())); + MultiOwnerTokenReceiverMSCAFactory factory = new MultiOwnerTokenReceiverMSCAFactory( + address(this), + address(multiOwnerPlugin), + address(tokenReceiverPlugin), + mscaImpl1, + ownerManifestHash, + tokenReceiverManifestHash, + entryPoint + ); + msca = UpgradeableModularAccount(payable(factory.createAccount(0, owners))); + vm.deal(address(msca), 2 ether); + + // setup mock tokens + token1 = new MockERC20("T1"); + token1.mint(address(msca), 1 ether); + } + + function test_sameStorageSlot_upgradeToAndCall() public { + vm.startPrank(owners[0]); + + // upgrade to mscaImpl2 + vm.expectEmit(true, true, true, true); + emit Upgraded(mscaImpl2); + msca.upgradeToAndCall(mscaImpl2, ""); + + // verify account storage is the same + (, bytes memory returnData) = address(msca).call(abi.encodeWithSelector(MultiOwnerPlugin.owners.selector)); + address[] memory returnedOwners = abi.decode(returnData, (address[])); + assertEq(Utils.reverseAddressArray(returnedOwners), owners); + assertEq(token1.balanceOf(address(msca)), 1 ether); + + // verify can do basic transaction + msca.execute(owners[0], 1 ether, ""); + assertEq(payable(msca).balance, 1 ether); + assertEq(payable(owners[0]).balance, 1 ether); + + vm.stopPrank(); + } +}