diff --git a/.gitmodules b/.gitmodules index f9baa1e..ca6218e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,10 +1,12 @@ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std - branch = v1.3.0 + branch = v1 [submodule "lib/openzeppelin-contracts"] path = lib/openzeppelin-contracts url = https://github.com/openzeppelin/openzeppelin-contracts + branch = release-v5.0 [submodule "lib/account-abstraction"] path = lib/account-abstraction url = https://github.com/eth-infinitism/account-abstraction + branch = releases/v0.7 diff --git a/.vscode/settings.json b/.vscode/settings.json index 284161a..53ea29c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,7 @@ { "solidity.packageDefaultDependenciesContractsDirectory": "src", "solidity.packageDefaultDependenciesDirectory": "lib", - "solidity.compileUsingRemoteVersion": "v0.8.21", + "solidity.compileUsingRemoteVersion": "v0.8.23", "editor.formatOnSave": true, "[solidity]": { "editor.defaultFormatter": "JuanBlanco.solidity" diff --git a/foundry.toml b/foundry.toml index d162c34..612809f 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,5 +1,5 @@ [profile.default] -solc = '0.8.21' +solc = '0.8.23' evm_version = 'paris' via_ir = true src = 'src' diff --git a/lib/account-abstraction b/lib/account-abstraction index abff2ac..7af70c8 160000 --- a/lib/account-abstraction +++ b/lib/account-abstraction @@ -1 +1 @@ -Subproject commit abff2aca61a8f0934e533d0d352978055fddbd96 +Subproject commit 7af70c8993a6f42973f520ae0752386a5032abe7 diff --git a/lib/forge-std b/lib/forge-std index 066ff16..ae570fe 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 066ff16c5c03e6f931cd041fd366bc4be1fae82a +Subproject commit ae570fec082bfe1c1f45b0acca4a2b4f84d345ce diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts index fd81a96..dbb6104 160000 --- a/lib/openzeppelin-contracts +++ b/lib/openzeppelin-contracts @@ -1 +1 @@ -Subproject commit fd81a96f01cc42ef1c9a5399364968d0e07e9e90 +Subproject commit dbb6104ce834628e473d2173bbc9d47f81a9eec3 diff --git a/src/CustomSlotInitializable.sol b/src/CustomSlotInitializable.sol index b666d1b..56760f0 100644 --- a/src/CustomSlotInitializable.sol +++ b/src/CustomSlotInitializable.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol) -pragma solidity ^0.8.21; +pragma solidity ^0.8.23; /** * @dev Identical to OpenZeppelin's `Initializable`, except that its state variables are kept at a custom storage slot diff --git a/src/LightAccount.sol b/src/LightAccount.sol index 58008c6..9e4b2e6 100644 --- a/src/LightAccount.sol +++ b/src/LightAccount.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.21; +pragma solidity ^0.8.23; /* solhint-disable avoid-low-level-calls */ /* solhint-disable no-inline-assembly */ @@ -8,11 +8,13 @@ pragma solidity ^0.8.21; import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; import {BaseAccount} from "account-abstraction/core/BaseAccount.sol"; +import {SIG_VALIDATION_FAILED} from "account-abstraction/core/Helpers.sol"; import {IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol"; -import {UserOperation} from "account-abstraction/interfaces/UserOperation.sol"; +import {PackedUserOperation} from "account-abstraction/interfaces/PackedUserOperation.sol"; import {TokenCallbackHandler} from "account-abstraction/samples/callback/TokenCallbackHandler.sol"; import {CustomSlotInitializable} from "./CustomSlotInitializable.sol"; @@ -49,6 +51,7 @@ import {CustomSlotInitializable} from "./CustomSlotInitializable.sol"; */ contract LightAccount is BaseAccount, TokenCallbackHandler, UUPSUpgradeable, CustomSlotInitializable, IERC1271 { using ECDSA for bytes32; + using MessageHashUtils for bytes32; // keccak256(abi.encode(uint256(keccak256("light_account_v1.storage")) - 1)) & ~bytes32(uint256(0xff)); bytes32 internal constant _STORAGE_POSITION = 0x691ec1a18226d004c07c9f8e5c4a6ff15a7b38db267cf7e3c945aef8be512200; @@ -311,7 +314,7 @@ contract LightAccount is BaseAccount, TokenCallbackHandler, UUPSUpgradeable, Cus * which the digest is wrapped with an "Ethereum Signed Message" envelope * for the EOA-owner case but not in the ERC-1271 contract-owner case. */ - function _validateSignature(UserOperation calldata userOp, bytes32 userOpHash) + function _validateSignature(PackedUserOperation calldata userOp, bytes32 userOpHash) internal virtual override @@ -320,7 +323,7 @@ contract LightAccount is BaseAccount, TokenCallbackHandler, UUPSUpgradeable, Cus address _owner = owner(); bytes32 signedHash = userOpHash.toEthSignedMessageHash(); bytes memory signature = userOp.signature; - (address recovered, ECDSA.RecoverError error) = signedHash.tryRecover(signature); + (address recovered, ECDSA.RecoverError error,) = signedHash.tryRecover(signature); if ( (error == ECDSA.RecoverError.NoError && recovered == _owner) || SignatureChecker.isValidERC1271SignatureNow(_owner, userOpHash, signature) diff --git a/src/LightAccountFactory.sol b/src/LightAccountFactory.sol index 3342e6e..91149a5 100644 --- a/src/LightAccountFactory.sol +++ b/src/LightAccountFactory.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.21; +pragma solidity ^0.8.23; import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; diff --git a/test/CustomSlotInitializable.t.sol b/test/CustomSlotInitializable.t.sol index afb2a31..b74db2f 100644 --- a/test/CustomSlotInitializable.t.sol +++ b/test/CustomSlotInitializable.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.23; import "forge-std/Test.sol"; diff --git a/test/LightAccount.t.sol b/test/LightAccount.t.sol index c99c049..1d0ceac 100644 --- a/test/LightAccount.t.sol +++ b/test/LightAccount.t.sol @@ -1,14 +1,15 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.23; import "forge-std/Test.sol"; import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; import {EntryPoint} from "account-abstraction/core/EntryPoint.sol"; import {IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol"; -import {UserOperation} from "account-abstraction/interfaces/UserOperation.sol"; +import {PackedUserOperation} from "account-abstraction/interfaces/PackedUserOperation.sol"; import {SimpleAccount} from "account-abstraction/samples/SimpleAccount.sol"; import {LightAccount} from "../src/LightAccount.sol"; @@ -17,6 +18,7 @@ import {LightAccountFactory} from "../src/LightAccountFactory.sol"; contract LightAccountTest is Test { using stdStorage for StdStorage; using ECDSA for bytes32; + using MessageHashUtils for bytes32; uint256 public constant EOA_PRIVATE_KEY = 1; address payable public constant BENEFICIARY = payable(address(0xbe9ef1c1a2ee)); @@ -55,9 +57,9 @@ contract LightAccountTest is Test { } function testExecuteCanBeCalledByEntryPointWithExternalOwner() public { - UserOperation memory op = + PackedUserOperation memory op = _getSignedOp(address(lightSwitch), abi.encodeCall(LightSwitch.turnOn, ()), EOA_PRIVATE_KEY); - UserOperation[] memory ops = new UserOperation[](1); + PackedUserOperation[] memory ops = new PackedUserOperation[](1); ops[0] = op; entryPoint.handleOps(ops, BENEFICIARY); assertTrue(lightSwitch.on()); @@ -65,17 +67,17 @@ contract LightAccountTest is Test { function testExecutedCanBeCalledByEntryPointWithContractOwner() public { _useContractOwner(); - UserOperation memory op = _getUnsignedOp(address(lightSwitch), abi.encodeCall(LightSwitch.turnOn, ())); + PackedUserOperation memory op = _getUnsignedOp(address(lightSwitch), abi.encodeCall(LightSwitch.turnOn, ())); op.signature = contractOwner.sign(entryPoint.getUserOpHash(op)); - UserOperation[] memory ops = new UserOperation[](1); + PackedUserOperation[] memory ops = new PackedUserOperation[](1); ops[0] = op; entryPoint.handleOps(ops, BENEFICIARY); assertTrue(lightSwitch.on()); } function testRejectsUserOpsWithInvalidSignature() public { - UserOperation memory op = _getSignedOp(address(lightSwitch), abi.encodeCall(LightSwitch.turnOn, ()), 1234); - UserOperation[] memory ops = new UserOperation[](1); + PackedUserOperation memory op = _getSignedOp(address(lightSwitch), abi.encodeCall(LightSwitch.turnOn, ()), 1234); + PackedUserOperation[] memory ops = new PackedUserOperation[](1); ops[0] = op; vm.expectRevert(abi.encodeWithSelector(IEntryPoint.FailedOp.selector, 0, "AA24 signature error")); entryPoint.handleOps(ops, BENEFICIARY); @@ -184,9 +186,9 @@ contract LightAccountTest is Test { function testEntryPointCanTransferOwnership() public { address newOwner = address(0x100); - UserOperation memory op = + PackedUserOperation memory op = _getSignedOp(address(account), abi.encodeCall(LightAccount.transferOwnership, (newOwner)), EOA_PRIVATE_KEY); - UserOperation[] memory ops = new UserOperation[](1); + PackedUserOperation[] memory ops = new PackedUserOperation[](1); ops[0] = op; vm.expectEmit(true, true, false, false); emit OwnershipTransferred(eoaAddress, newOwner); @@ -282,10 +284,10 @@ contract LightAccountTest is Test { keccak256( abi.encodePacked( type(LightAccountFactory).creationCode, - bytes32(uint256(uint160(0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789))) + bytes32(uint256(uint160(0x0000000071727De22E5E9d8BAf0edAc6f37da032))) ) ), - 0x23fb754854a6aa03057b1bae5d971963d92e534dc714fa59fff6c08a3617ba3e + 0xbb16617edd0a177192b9a37ddb37f7783ae99a0cd21d53607df759ded54e024c ); } @@ -294,17 +296,23 @@ contract LightAccountTest is Test { account.transferOwnership(address(contractOwner)); } - function _getUnsignedOp(address target, bytes memory innerCallData) internal view returns (UserOperation memory) { - return UserOperation({ + function _getUnsignedOp(address target, bytes memory innerCallData) + internal + view + returns (PackedUserOperation memory) + { + uint128 verificationGasLimit = 1 << 24; + uint128 callGasLimit = 1 << 24; + uint128 maxPriorityFeePerGas = 1 << 8; + uint128 maxFeePerGas = 1 << 8; + return PackedUserOperation({ sender: address(account), nonce: 0, initCode: "", callData: abi.encodeCall(LightAccount.execute, (target, 0, innerCallData)), - callGasLimit: 1 << 24, - verificationGasLimit: 1 << 24, + accountGasLimits: bytes32(uint256(verificationGasLimit) << 128 | callGasLimit), preVerificationGas: 1 << 24, - maxFeePerGas: 1 << 8, - maxPriorityFeePerGas: 1 << 8, + gasFees: bytes32(uint256(maxPriorityFeePerGas) << 128 | maxFeePerGas), paymasterAndData: "", signature: "" }); @@ -313,9 +321,9 @@ contract LightAccountTest is Test { function _getSignedOp(address target, bytes memory innerCallData, uint256 privateKey) internal view - returns (UserOperation memory) + returns (PackedUserOperation memory) { - UserOperation memory op = _getUnsignedOp(target, innerCallData); + PackedUserOperation memory op = _getUnsignedOp(target, innerCallData); op.signature = _sign(privateKey, entryPoint.getUserOpHash(op).toEthSignedMessageHash()); return op; } diff --git a/test/LightAccountFactory.t.sol b/test/LightAccountFactory.t.sol index ec019dd..69b9c0e 100644 --- a/test/LightAccountFactory.t.sol +++ b/test/LightAccountFactory.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.23; import "forge-std/Test.sol";