From 75e89b32940ec07dd5fd20be09c3292a5c1275d5 Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Mon, 11 Dec 2023 14:41:45 -0800 Subject: [PATCH 001/106] style: run forge fmt (#11) --- src/factory/MultiOwnerMSCAFactory.sol | 2 +- src/factory/MultiOwnerTokenReceiverMSCAFactory.sol | 2 +- test/account/AccountExecHooks.t.sol | 8 ++++---- test/account/AccountLoupe.t.sol | 8 ++++---- test/account/AccountPermittedCallHooks.t.sol | 8 ++++---- test/account/AccountReturnData.t.sol | 6 +++--- test/account/ExecuteFromPluginPermissions.t.sol | 8 ++++---- test/account/ManifestValidity.t.sol | 8 ++++---- test/account/UpgradeableModularAccount.t.sol | 2 +- test/account/ValidationIntersection.t.sol | 6 +++--- test/factory/MultiOwnerMSCAFactoryTest.t.sol | 6 +----- test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol | 8 ++++---- test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol | 9 +++++++-- .../permissions/SessionKeyNativeTokenSpendLimits.t.sol | 9 +++++++-- .../permissions/SessionKeyPermissionsPlugin.t.sol | 9 +++++++-- test/upgrade/MSCAToMSCA.t.sol | 6 +++--- 16 files changed, 58 insertions(+), 47 deletions(-) diff --git a/src/factory/MultiOwnerMSCAFactory.sol b/src/factory/MultiOwnerMSCAFactory.sol index e859ce92..1429124c 100644 --- a/src/factory/MultiOwnerMSCAFactory.sol +++ b/src/factory/MultiOwnerMSCAFactory.sol @@ -55,7 +55,7 @@ contract MultiOwnerMSCAFactory is Ownable { // short circuit if exists if (addr.code.length == 0) { // not necessary to check return addr of this arg since next call fails if so - new ERC1967Proxy{salt : combinedSalt}(IMPL, ""); + new ERC1967Proxy{salt: combinedSalt}(IMPL, ""); address[] memory plugins = new address[](1); plugins[0] = MULTI_OWNER_PLUGIN; diff --git a/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol b/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol index 53a25927..2461b6a8 100644 --- a/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol +++ b/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol @@ -61,7 +61,7 @@ contract MultiOwnerTokenReceiverMSCAFactory is Ownable { // short circuit if exists if (addr.code.length == 0) { // not necessary to check return addr of this arg since next call fails if so - new ERC1967Proxy{salt : combinedSalt}(IMPL, ""); + new ERC1967Proxy{salt: combinedSalt}(IMPL, ""); address[] memory plugins = new address[](2); plugins[0] = MULTI_OWNER_PLUGIN; diff --git a/test/account/AccountExecHooks.t.sol b/test/account/AccountExecHooks.t.sol index cdbaf18f..1130fc26 100644 --- a/test/account/AccountExecHooks.t.sol +++ b/test/account/AccountExecHooks.t.sol @@ -65,10 +65,10 @@ contract UpgradeableModularAccountExecHooksTest is Test { address impl = address(new UpgradeableModularAccount(IEntryPoint(address(entryPoint)))); factory = new MultiOwnerMSCAFactory( - address(this), - address(multiOwnerPlugin), - impl, - keccak256(abi.encode(multiOwnerPlugin.pluginManifest())), + address(this), + address(multiOwnerPlugin), + impl, + keccak256(abi.encode(multiOwnerPlugin.pluginManifest())), entryPoint ); diff --git a/test/account/AccountLoupe.t.sol b/test/account/AccountLoupe.t.sol index 23ec5ef4..efbf8702 100644 --- a/test/account/AccountLoupe.t.sol +++ b/test/account/AccountLoupe.t.sol @@ -44,10 +44,10 @@ contract AccountLoupeTest is Test { multiOwnerPlugin = new MultiOwnerPlugin(); address impl = address(new UpgradeableModularAccount(entryPoint)); factory = new MultiOwnerMSCAFactory( - address(this), - address(multiOwnerPlugin), - impl, - keccak256(abi.encode(multiOwnerPlugin.pluginManifest())), + address(this), + address(multiOwnerPlugin), + impl, + keccak256(abi.encode(multiOwnerPlugin.pluginManifest())), entryPoint ); comprehensivePlugin = new ComprehensivePlugin(); diff --git a/test/account/AccountPermittedCallHooks.t.sol b/test/account/AccountPermittedCallHooks.t.sol index 8fcacc86..11fc86bf 100644 --- a/test/account/AccountPermittedCallHooks.t.sol +++ b/test/account/AccountPermittedCallHooks.t.sol @@ -66,10 +66,10 @@ contract UpgradeableModularAccountPermittedCallHooksTest is Test { address impl = address(new UpgradeableModularAccount(IEntryPoint(address(entryPoint)))); factory = new MultiOwnerMSCAFactory( - address(this), - address(multiOwnerPlugin), - impl, - keccak256(abi.encode(multiOwnerPlugin.pluginManifest())), + address(this), + address(multiOwnerPlugin), + impl, + keccak256(abi.encode(multiOwnerPlugin.pluginManifest())), entryPoint ); diff --git a/test/account/AccountReturnData.t.sol b/test/account/AccountReturnData.t.sol index 4bcd41b8..c49548b6 100644 --- a/test/account/AccountReturnData.t.sol +++ b/test/account/AccountReturnData.t.sol @@ -38,9 +38,9 @@ contract AccountReturnDataTest is Test { factory = new MultiOwnerMSCAFactory( address(this), - address(multiOwnerPlugin), - impl, - keccak256(abi.encode(multiOwnerPlugin.pluginManifest())), + address(multiOwnerPlugin), + impl, + keccak256(abi.encode(multiOwnerPlugin.pluginManifest())), entryPoint ); diff --git a/test/account/ExecuteFromPluginPermissions.t.sol b/test/account/ExecuteFromPluginPermissions.t.sol index 0862a93b..e8bafa35 100644 --- a/test/account/ExecuteFromPluginPermissions.t.sol +++ b/test/account/ExecuteFromPluginPermissions.t.sol @@ -57,10 +57,10 @@ contract ExecuteFromPluginPermissionsTest is Test { address impl = address(new UpgradeableModularAccount(entryPoint)); factory = new MultiOwnerMSCAFactory( - address(this), - address(multiOwnerPlugin), - impl, - keccak256(abi.encode(multiOwnerPlugin.pluginManifest())), + address(this), + address(multiOwnerPlugin), + impl, + keccak256(abi.encode(multiOwnerPlugin.pluginManifest())), entryPoint ); diff --git a/test/account/ManifestValidity.t.sol b/test/account/ManifestValidity.t.sol index 8ff2c516..dd4dbdb4 100644 --- a/test/account/ManifestValidity.t.sol +++ b/test/account/ManifestValidity.t.sol @@ -37,10 +37,10 @@ contract ManifestValidityTest is Test { address impl = address(new UpgradeableModularAccount(entryPoint)); factory = new MultiOwnerMSCAFactory( - address(this), - address(multiOwnerPlugin), - impl, - keccak256(abi.encode(multiOwnerPlugin.pluginManifest())), + address(this), + address(multiOwnerPlugin), + impl, + keccak256(abi.encode(multiOwnerPlugin.pluginManifest())), entryPoint ); diff --git a/test/account/UpgradeableModularAccount.t.sol b/test/account/UpgradeableModularAccount.t.sol index 799aa4cd..40994022 100644 --- a/test/account/UpgradeableModularAccount.t.sol +++ b/test/account/UpgradeableModularAccount.t.sol @@ -99,7 +99,7 @@ contract UpgradeableModularAccountTest is Test { } function test_initialize_revertArrayLengthMismatch() public { - ERC1967Proxy account = new ERC1967Proxy{salt : ""}(accountImplementation, ""); + ERC1967Proxy account = new ERC1967Proxy{salt: ""}(accountImplementation, ""); address[] memory plugins = new address[](2); bytes memory pluginInitData = abi.encode(new bytes32[](1), new bytes[](1)); vm.expectRevert(PluginManagerInternals.ArrayLengthMismatch.selector); diff --git a/test/account/ValidationIntersection.t.sol b/test/account/ValidationIntersection.t.sol index 66e96e66..3b0097f6 100644 --- a/test/account/ValidationIntersection.t.sol +++ b/test/account/ValidationIntersection.t.sol @@ -40,9 +40,9 @@ contract ValidationIntersectionTest is Test { address impl = address(new UpgradeableModularAccount(entryPoint)); MultiOwnerMSCAFactory factory = new MultiOwnerMSCAFactory( - address(this), - address(multiOwnerPlugin), - impl, + address(this), + address(multiOwnerPlugin), + impl, keccak256(abi.encode(multiOwnerPlugin.pluginManifest())), entryPoint ); diff --git a/test/factory/MultiOwnerMSCAFactoryTest.t.sol b/test/factory/MultiOwnerMSCAFactoryTest.t.sol index c5477ece..62aff26a 100644 --- a/test/factory/MultiOwnerMSCAFactoryTest.t.sol +++ b/test/factory/MultiOwnerMSCAFactoryTest.t.sol @@ -37,11 +37,7 @@ contract MultiOwnerMSCAFactoryTest is Test { multiOwnerPlugin = new MultiOwnerPlugin(); bytes32 manifestHash = keccak256(abi.encode(multiOwnerPlugin.pluginManifest())); factory = new MultiOwnerMSCAFactory( - address(this), - address(multiOwnerPlugin), - impl, - manifestHash, - IEntryPoint(address(entryPoint)) + address(this), address(multiOwnerPlugin), impl, manifestHash, IEntryPoint(address(entryPoint)) ); vm.deal(address(this), 100 ether); } diff --git a/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol b/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol index d3dfbd4d..3d5116ff 100644 --- a/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol +++ b/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol @@ -54,11 +54,11 @@ contract MultiOwnerTokenReceiverMSCAFactoryTest is Test { bytes32 ownerManifestHash = keccak256(abi.encode(multiOwnerPlugin.pluginManifest())); bytes32 tokenReceiverManifestHash = keccak256(abi.encode(tokenReceiverPlugin.pluginManifest())); factory = new MultiOwnerTokenReceiverMSCAFactory( - address(this), - address(multiOwnerPlugin), + address(this), + address(multiOwnerPlugin), address(tokenReceiverPlugin), - impl, - ownerManifestHash, + impl, + ownerManifestHash, tokenReceiverManifestHash, IEntryPoint(address(entryPoint)) ); diff --git a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol index 566826ec..0e39b234 100644 --- a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol +++ b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol @@ -50,8 +50,13 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { multiOwnerPlugin = new MultiOwnerPlugin(); address impl = address(new UpgradeableModularAccount(entryPoint)); - factory = - new MultiOwnerMSCAFactory(address(this), address(multiOwnerPlugin), impl, keccak256(abi.encode(multiOwnerPlugin.pluginManifest())), entryPoint); + factory = new MultiOwnerMSCAFactory( + address(this), + address(multiOwnerPlugin), + impl, + keccak256(abi.encode(multiOwnerPlugin.pluginManifest())), + entryPoint + ); sessionKeyPlugin = new SessionKeyPlugin(); diff --git a/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol b/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol index a267d90e..563d57dc 100644 --- a/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol @@ -60,8 +60,13 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { multiOwnerPlugin = new MultiOwnerPlugin(); bytes32 multiOwnerPluginManifestHash = keccak256(abi.encode(multiOwnerPlugin.pluginManifest())); - factory = - new MultiOwnerMSCAFactory(address(this), address(multiOwnerPlugin), address(new UpgradeableModularAccount(entryPoint)), multiOwnerPluginManifestHash, entryPoint); + factory = new MultiOwnerMSCAFactory( + address(this), + address(multiOwnerPlugin), + address(new UpgradeableModularAccount(entryPoint)), + multiOwnerPluginManifestHash, + entryPoint + ); sessionKeyPlugin = new SessionKeyPlugin(); sessionKeyPermissionsPlugin = new SessionKeyPermissionsPlugin(); diff --git a/test/plugin/session/permissions/SessionKeyPermissionsPlugin.t.sol b/test/plugin/session/permissions/SessionKeyPermissionsPlugin.t.sol index e9f28a2f..6cc525ab 100644 --- a/test/plugin/session/permissions/SessionKeyPermissionsPlugin.t.sol +++ b/test/plugin/session/permissions/SessionKeyPermissionsPlugin.t.sol @@ -64,8 +64,13 @@ contract SessionKeyPermissionsPluginTest is Test { multiOwnerPlugin = new MultiOwnerPlugin(); address impl = address(new UpgradeableModularAccount(entryPoint)); - factory = - new MultiOwnerMSCAFactory(address(this), address(multiOwnerPlugin), impl, keccak256(abi.encode(multiOwnerPlugin.pluginManifest())), entryPoint); + factory = new MultiOwnerMSCAFactory( + address(this), + address(multiOwnerPlugin), + impl, + keccak256(abi.encode(multiOwnerPlugin.pluginManifest())), + entryPoint + ); sessionKeyPlugin = new SessionKeyPlugin(); sessionKeyPermissionsPlugin = new SessionKeyPermissionsPlugin(); diff --git a/test/upgrade/MSCAToMSCA.t.sol b/test/upgrade/MSCAToMSCA.t.sol index b9ecf730..d4e9134e 100644 --- a/test/upgrade/MSCAToMSCA.t.sol +++ b/test/upgrade/MSCAToMSCA.t.sol @@ -42,10 +42,10 @@ contract MSCAToMSCATest is Test { bytes32 tokenReceiverManifestHash = keccak256(abi.encode(tokenReceiverPlugin.pluginManifest())); MultiOwnerTokenReceiverMSCAFactory factory = new MultiOwnerTokenReceiverMSCAFactory( address(this), - address(multiOwnerPlugin), + address(multiOwnerPlugin), address(tokenReceiverPlugin), - mscaImpl1, - ownerManifestHash, + mscaImpl1, + ownerManifestHash, tokenReceiverManifestHash, entryPoint ); From 009abaa1ef0366c191137dd947b361c8bbd5ab0a Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Mon, 11 Dec 2023 15:12:06 -0800 Subject: [PATCH 002/106] chore: setup deploy script and deploy to sepolia (#10) --- .env.example | 19 +- .gitignore | 5 +- README.md | 6 + .../Deploy.s.sol/11155111/run-1700676223.json | 217 +++++++++ .../Deploy.s.sol/11155111/run-1700676229.json | 444 ++++++++++++++++++ .../Deploy.s.sol/11155111/run-1700676621.json | 55 +++ .../Deploy.s.sol/11155111/run-1700676627.json | 115 +++++ .../Deploy.s.sol/11155111/run-latest.json | 115 +++++ foundry.toml | 5 +- migrations/sepolia/contracts.json | 25 + script/Counter.s.sol | 12 - script/Deploy.s.sol | 147 ++++++ 12 files changed, 1147 insertions(+), 18 deletions(-) create mode 100644 broadcast/Deploy.s.sol/11155111/run-1700676223.json create mode 100644 broadcast/Deploy.s.sol/11155111/run-1700676229.json create mode 100644 broadcast/Deploy.s.sol/11155111/run-1700676621.json create mode 100644 broadcast/Deploy.s.sol/11155111/run-1700676627.json create mode 100644 broadcast/Deploy.s.sol/11155111/run-latest.json create mode 100644 migrations/sepolia/contracts.json delete mode 100644 script/Counter.s.sol create mode 100644 script/Deploy.s.sol diff --git a/.env.example b/.env.example index ebc9b5b0..f4c33e3f 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,19 @@ DEPLOYER_PRIVATE_KEY= +OWNER= -RPC_URL_MAINNET= -RPC_URL_GOERLI= +# 1 ether required by bundlers +REQUIRED_STAKE_AMOUNT=1 +# 1 day required by bundlers +UNSTAKE_DELAY_SEC=86400 -ETHERSCAN_API_KEY= +ENTRYPOINT= +# MSCA_IMPL= +# OWNER_FACTORY= +# OWNER_TOKEN_RECEIVER_FACTORY= + +# OWNER_PLUGIN= +# TOKEN_RECEIVER_PLUGIN= +# SESSION_KEY_PLUGIN= +# SESSION_KEY_PERMS_PLUGIN= + +SEPOLIA_RPC_URL= \ No newline at end of file diff --git a/.gitignore b/.gitignore index 53f6fb42..b8896348 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,7 @@ node_modules/ # coverage report/ -lcov.info \ No newline at end of file +lcov.info + +# secret +.env \ No newline at end of file diff --git a/README.md b/README.md index 95f0746b..1b4018e5 100644 --- a/README.md +++ b/README.md @@ -49,3 +49,9 @@ slither . ## External Libraries We use Solady's highly optimized [UUPSUpgradeable](https://github.com/Vectorized/solady/blob/a061f38f27cd7ae330a86d42d3f15b4e7237f064/src/utils/UUPSUpgradeable.sol) in our contracts + +## Deployment + +```bash +forge script script/Deploy.s.sol --rpc-url $SEPOLIA_RPC_URL --broadcast +``` diff --git a/broadcast/Deploy.s.sol/11155111/run-1700676223.json b/broadcast/Deploy.s.sol/11155111/run-1700676223.json new file mode 100644 index 00000000..799f070a --- /dev/null +++ b/broadcast/Deploy.s.sol/11155111/run-1700676223.json @@ -0,0 +1,217 @@ +{ + "transactions": [ + { + "hash": "0x09d250a7c7e7c8414232c10a3487fc301d10bd70b32b8f66c01db654f5471fbe", + "transactionType": "CREATE", + "contractName": "UpgradeableModularAccount", + "contractAddress": "0xb2b748c2557c552B8636862E41aB3649319dD045", + "function": null, + "arguments": [ + "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" + ], + "transaction": { + "type": "0x02", + "from": "0x7f89ed1f3f0d52d303904101305471bca3cde710", + "gas": "0x6888c4", + "value": "0x0", + "data": "0x60c0346200010557601f62005f0038819003918201601f19168301916001600160401b038311848410176200010a578084926020946040528339810103126200010557516001600160a01b038116810362000105573060805260a0527fade46bbfcf6f898a43d541e42556d456ca0bf9b326df8debc0f29d3f811a0300805460ff8160081c16620000f35760ff80821603620000e2575b604051615ddf90816200012182396080518181816108a801526109ac015260a05181818161076801528181611065015281816111ac015281816119e50152818161375301526148ab0152f35b60ff191660ff179055388062000096565b60405163593ae07560e01b8152600490fd5b600080fd5b634e487b7160e01b600052604160045260246000fdfe60806040526004361015610026575b36156100245761001c613720565b602081519101f35b005b60003560e01c806301ffc9a7146101465780630b0faea11461014157806334fcd5be1461013c57806338997b11146101375780633a0cac56146101325780633a871cdd1461012d5780634f1ef2861461012857806352d1902d14610123578063642f9dd41461011e57806377da2b74146101195780638d1121841461011457806394ed11e71461010f578063ad60fa261461010a578063b0d691fe14610105578063b61d27f614610100578063ceaf1309146100fb578063d087d288146100f65763e69e24a80361000e57611353565b611178565b611118565b611094565b61104f565b610e64565b610cb1565b610bd3565b610a27565b6109fa565b610998565b61086c565b61072e565b61069a565b61048e565b61033b565b610209565b610162565b6001600160e01b031981160361015d57565b600080fd5b3461015d57602036600319011261015d5760206101896004356101848161014b565b61485d565b6040519015158152f35b6001600160a01b0381160361015d57565b35906101af82610193565b565b60208082019080835283518092528060408094019401926000905b8382106101db57505050505090565b845180516001600160581b0319908116885290840151168684015294850194938201936001909101906101cc565b3461015d57604036600319011261015d5761025d610251600161024b61024660043561023481610193565b602435906102418261014b565b61569c565b61147b565b0161155d565b604051918291826101b1565b0390f35b9181601f8401121561015d578235916001600160401b03831161015d576020808501948460051b01011161015d57565b60005b8381106102a45750506000910152565b8181015183820152602001610294565b906020916102cd81518092818552858086019101610291565b601f01601f1916010190565b602080820190808352835180925260408301928160408460051b8301019501936000915b84831061030d5750505050505090565b909192939495848061032b600193603f198682030187528a516102b4565b98019301930191949392906102fd565b60208060031936011261015d576004356001600160401b03811161015d57610367903690600401610261565b91610370614899565b93909161037c826136d6565b9360005b8381106103a15761025d866103958988614903565b604051918291826102d9565b806103f26103b26001938786613a0e565b356103bc81610193565b856103c8848988613a0e565b01356103ec6103e56103db868b8a613a0e565b6040810190613955565b36916117d4565b916139b3565b6103fc82896114ca565b5261040781886114ca565b5001610380565b9181601f8401121561015d578235916001600160401b03831161015d576020838186019501011161015d57565b606060031982011261015d5760043561045381610193565b9160243591604435906001600160401b03821161015d576104769160040161040e565b9091565b90602061048b9281815201906102b4565b90565b6104973661043b565b919283151580610686575b8061066d575b610655576104da6104d3826104bc33611f3f565b9060018060a01b0316600052602052604060002090565b5460ff1690565b15610649576104f96104ef826104bc33611f3f565b5460081c60ff1690565b8015610641575b8015610604575b156105e15760036105bc6105d5936102466105c29461025d986105756338997b1160401b6001600160601b03193360601b16179461056f61054f61054961213b565b88614f64565b9590946103ec61056561056061213b565b614ee2565b95909f36916117d4565b9a6151e4565b6338997b1160e01b600052600080516020615d4a8339815191526020526105d06105c260077f366b46b479d417a249e7f56f296f035e13c924e69b7ed63bca6e286fe8e383b15b01615c94565b6105ca611983565b906151e4565b6151e4565b6040519182918261047a565b91610600916040519485946320238ecf60e21b86523360048701613a30565b0390fd5b5061063c6104d3600161061a846104bc33611f3f565b016106258686613987565b63ffffffff60e01b16600052602052604060002090565b610507565b508215610500565b61063c6104d333611f06565b60405163171b202760e11b8152336004820152602490fd5b5061068161067d6104ef33611f06565b1590565b6104a8565b503484116104a2565b600091031261015d57565b3461015d57600036600319011261015d576106b3615bf8565b805160005b81811061070c5782604051809160208083018184528251809152816040850193019160005b8281106106ec57505050500390f35b83516001600160a01b0316855286955093810193928101926001016106dd565b80610719600192856114ca565b5160601c61072782866114ca565b52016106b8565b3461015d5760031960603682011261015d576004356001600160401b03811161015d576101608160040192823603011261015d57604435907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361085a5760640160046107a58285613955565b90501061081c57826107c66107c06107ea9361025d96613955565b906152dc565b9060016107d28361141d565b01549060ff8260a81c16926024359260581b90614940565b9080610802575b506040519081529081906020820190565b600080808093338219f15061081561214e565b50386107f1565b61083261082c6106009285613955565b90613987565b60405163fcfc5aad60e01b81526001600160e01b031990911660048201529081906024820190565b60405163ea800da560e01b8152600490fd5b604036600319011261015d5760043561088481610193565b6024356001600160401b03811161015d576108a390369060040161040e565b9091307f00000000000000000000000000000000000000000000000000000000000000001461098a576108d4614899565b9160018060a01b03166352d1902d6001527f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc9384602060016004601d865afa510361097c578082600096817fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b8980a255610957575b846109548585614903565b80f35b908185926040519788378638925af415610972578380610949565b50503d90823e3d90fd5b6355299b496001526004601dfd5b639f03a0266000526004601cfd5b3461015d57600036600319011261015d57307f00000000000000000000000000000000000000000000000000000000000000000361098a5760206040517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152f35b3461015d57602036600319011261015d5761025d610251600561024b600435610a228161014b565b61141d565b3461015d57608036600319011261015d57600435610a4481610193565b6001600160401b039060243582811161015d57610a6590369060040161040e565b9260443581811161015d57610a7e90369060040161040e565b9160643590811161015d57610a97903690600401610261565b929091610aa2614899565b969095610aad613a61565b6001600160a01b03831681529860009181610b6e575b505015610af9575b5090610ae99392918760606100249901805115610aee575b50613cbe565b614903565b600019905238610ae3565b604051630c77631360e41b81529493929190600090869060049082906001600160a01b03165afa978815610b695761002498610ae996600091610b48575b506020820152975090919293610acb565b610b63913d8091833e610b5b81836112e2565b810190611d67565b38610b37565b611efa565b610b7a91810190613ae1565b8051805180610baf575b505080610ba1610b9960206040940151151590565b15158c840152565b015160608a01523880610ac3565b610bc3929350602080918301019101611d67565b60208a0152600190604038610b84565b3461015d57602036600319011261015d5761025d600435610bf38161014b565b610c58610c476002610c3f610c066113fe565b94610c108161531c565b15610c97573086525b610a226001610c278361141d565b015460581b60208801906001600160581b0319169052565b015460581b90565b6001600160581b0319166040830152565b6040805182516001600160a01b031681526020808401516001600160581b03199081169183019190915292820151909216908201529081906060820190565b6001600160a01b03610ca88261141d565b54168652610c19565b602036600319011261015d576004356001600160401b03811161015d57610cdc90369060040161040e565b610ce681836152dc565b90610cf1823361569c565b610cfa8161147b565b9182549460ff861615610e3c5790610d11916138c4565b60609160ff839660081c16610e28575b50610d2b8461141d565b805490916001600160a01b038216908115610e065760609660ff889460a01c16610de5575b5090610d5e610d64926138fd565b9061383a565b94610d6d613937565b9515610ddd5791610d86610d99949261025d98946151e4565b805460a81c60ff16610dc8575b506151e4565b805460101c60ff16610db3575b506040519182918261047a565b6105c26003610dc29201615c94565b38610da6565b6105c26007610dd79201615c94565b38610d93565b855160208701fd5b81610d6493929850610d5e9450610dfb91614f4a565b939097919250610d50565b60405163fcfc5aad60e01b81526001600160e01b031988166004820152602490fd5b819550610e359250614f64565b9338610d21565b60405163742f979f60e11b81523360048201526001600160e01b031986166024820152604490fd5b3461015d5760a08060031936011261015d57600435610e8281610193565b6001600160401b039060443582811161015d57610ea390369060040161040e565b91909260643581811161015d57610ebe903690600401610261565b60849391933583811161015d57610ed9903690600401610261565b93610eef610ee5614899565b99909836916117d4565b94610ef98461133c565b96604090610f098251998a6112e2565b858952602095868a019060051b82019136831161015d57905b82821061102f57505050610f358261133c565b9a610f4282519c8d6112e2565b828c52858c019260051b85019436861161015d5780935b868510610f76576100248d8f610ae98f918f8f8f60243590612279565b843586811161015d5782018036039160c0831261015d578551610f988161127d565b8235610fa381610193565b81528a830135610fb28161014b565b8b82015260608094603f19011261015d578651610fce81611242565b87840135610fdb81611bdb565b815284840135610fea81611af6565b8c8201526080840135610ffc81611bdb565b8882015287820152858301359389851161015d576110208c9594869536910161180b565b90820152815201940193610f59565b81356001600160581b03198116810361015d578152908701908701610f22565b3461015d57600036600319011261015d576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b61025d6110c06110c66110a63661043b565b94916103ec6110b6949294614899565b95909736916117d4565b92614903565b6040519182916020835260208301906102b4565b90815180825260208080930193019160005b8281106110fa575050505090565b83516001600160581b031916855293810193928101926001016110ec565b3461015d57602036600319011261015d5761116a61113b600435610a228161014b565b61025d611156600461114f60038501615c94565b9301615c94565b6040519384936040855260408501906110da565b9083820360208501526110da565b3461015d5760008060031936011261122957604051631aab3f0d60e11b8152306004820152602481018290526020816044817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115610b695782916111ef575b604051828152602090f35b90506020813d8211611221575b81611209602093836112e2565b8101031261121d5761025d915051386111e4565b5080fd5b3d91506111fc565b80fd5b634e487b7160e01b600052604160045260246000fd5b606081019081106001600160401b0382111761125d57604052565b61122c565b604081019081106001600160401b0382111761125d57604052565b608081019081106001600160401b0382111761125d57604052565b6001600160401b03811161125d57604052565b6101a081019081106001600160401b0382111761125d57604052565b602081019081106001600160401b0382111761125d57604052565b90601f801991011681019081106001600160401b0382111761125d57604052565b604051906101af826112ab565b6040519060a082018281106001600160401b0382111761125d57604052565b604051906101af82611242565b6001600160401b03811161125d5760051b60200190565b3461015d57604036600319011261015d576001600160401b0360043581811161015d573660238201121561015d57806004013561138f8161133c565b9161139d60405193846112e2565b81835260209160248385019160051b8301019136831161015d57602401905b8282106113e5576024358587821161015d576113df61002492369060040161040e565b916116fa565b83809183356113f381610193565b8152019101906113bc565b6040519061140b82611242565b60006040838281528260208201520152565b63ffffffff60e01b16600052600080516020615d4a833981519152602052604060002090565b63ffffffff60e01b166000527fade46bbfcf6f898a43d541e42556d456ca0bf9b326df8debc0f29d3f811a0306602052604060002090565b6001600160401b0319166000527fade46bbfcf6f898a43d541e42556d456ca0bf9b326df8debc0f29d3f811a0304602052604060002090565b634e487b7160e01b600052603260045260246000fd5b80518210156114de5760209160051b010190565b6114b4565b906114ed8261133c565b6040906114fc825191826112e2565b838152809361150d601f199161133c565b0191600090815b848110611522575050505050565b602090825161153081611262565b848152828581830152828701015201611514565b906001600160581b031916600052602052604060002090565b9061156782615c94565b9161157460028201615c94565b8351815191906000835b8282106116c85761158f91506114e3565b95600094855b8381106115ff5750505050906000915b8183106115b3575050508252565b9091926115f16115d46115c686856114ca565b516001600160581b03191690565b60206115e0848a6114ca565b5101906001600160581b0319169052565b6001809101930191906115a5565b60019061162261161d6116156115c684886114ca565b848601611544565b615c94565b8051908b82156116a05750906000915b8c82841061164557505050505b01611595565b8b611696879394959d9260208f86956116906115c68f9361168a6116708f6115c6906115e0986114ca565b61167a868a6114ca565b51906001600160581b0319169052565b8b6114ca565b936114ca565b019a019190611632565b8a92506116c1915061167a849b6116bb6115c688978b6114ca565b926114ca565b019661163f565b6001906116f16116eb6116de6115c6868d6114ca565b6001600160581b03191690565b886157aa565b0191019061157e565b91600080516020615d2a8339815191529182549360ff8560081c1615948580966117ac575b8015611795575b156117845760ff1916600117909355611743928461176357611996565b61174957565b600080516020615d2a833981519152805461ff0019169055565b600080516020615d2a833981519152805461ff001916610100179055611996565b60405162dc149f60e41b8152600490fd5b50303b1580156117265750600160ff821614611726565b50600160ff82161061171f565b6001600160401b03811161125d57601f01601f191660200190565b9291926117e0826117b9565b916117ee60405193846112e2565b82948184528183011161015d578281602093846000960137010152565b9080601f8301121561015d5781602061048b933591016117d4565b81601f8201121561015d578035916020916118408461133c565b9361184e60405195866112e2565b808552838086019160051b8301019280841161015d57848301915b8483106118795750505050505090565b82356001600160401b03811161015d57869161189a8484809489010161180b565b815201920191611869565b91909160408184031261015d576001600160401b0392813584811161015d5782019381601f8601121561015d5760209480356118e08161133c565b916118ee60405193846112e2565b818352878084019260051b8201019185831161015d5788809201905b83821061192a57505050509483013590811161015d5761048b9201611826565b8135815290820190820161190a565b604051611945816112c7565b60008152906000368137565b9061195b8261133c565b61196860405191826112e2565b8281528092611979601f199161133c565b0190602036910137565b60405161198f816112c7565b6000815290565b9291906119a5918101906118a5565b83519180518314801590611a82575b611a70576119c0611939565b906119c9611983565b9260005b858110611a2c57505050505050905060018060a01b037f0000000000000000000000000000000000000000000000000000000000000000167f9f08b8dca66d3393166c297eebdbe382963a15cce40f3a2f4bf32378553fe65a600080a2565b80611a6a8686611a4e611a416001968e6114ca565b516001600160a01b031690565b611a5885896114ca565b51611a6386896114ca565b5191612279565b016119cd565b60405163512509d360e11b8152600490fd5b5081518314156119b4565b9080601f8301121561015d57815190602091611aa88161133c565b93611ab660405195866112e2565b818552838086019260051b82010192831161015d578301905b828210611add575050505090565b8380918351611aeb8161014b565b815201910190611acf565b8015150361015d57565b51906101af82611af6565b81601f8201121561015d57805191602091611b258461133c565b93604092611b35845196876112e2565b818652848087019260051b8401019381851161015d57858401925b858410611b61575050505050505090565b83516001600160401b039081811161015d57860191606080601f19858803011261015d57845190611b9182611242565b8a850151611b9e81610193565b825285850151611bad81611af6565b8b83015284015192831161015d57611bcc868b80969581960101611a8d565b85820152815201930192611b50565b60ff81160361015d57565b919082606091031261015d57604051611bfe81611242565b80928051600581101561015d57604091829184526020810151611c2081611bdb565b60208501520151910152565b9080601f8301121561015d578151916020611c468461133c565b93604093611c56855196876112e2565b818652828087019260071b8501019381851161015d578301915b848310611c805750505050505090565b60808383031261015d57836080918751611c9981611262565b8551611ca48161014b565b8152611cb285848801611be6565b83820152815201920191611c70565b81601f8201121561015d578051906020611cda8361133c565b93604090611cea825196876112e2565b848652828601918360e08097028601019482861161015d578401925b858410611d17575050505050505090565b868484031261015d578487918351611d2e81611242565b8651611d398161014b565b8152611d4786848901611be6565b83820152611d588660808901611be6565b85820152815201930192611d06565b60208183031261015d5780516001600160401b039182821161015d57016101a08184031261015d57611d97611303565b92815183811161015d5781611dad918401611a8d565b8452602082015183811161015d5781611dc7918401611a8d565b6020850152604082015183811161015d5781611de4918401611a8d565b6040850152606082015183811161015d5781611e01918401611a8d565b6060850152611e1260808301611b00565b6080850152611e2360a08301611b00565b60a085015260c082015183811161015d5781611e40918401611b0b565b60c085015260e082015183811161015d5781611e5d918401611c2c565b60e08501526101008083015184811161015d5782611e7c918501611c2c565b908501526101208083015184811161015d5782611e9a918501611c2c565b908501526101408083015184811161015d5782611eb8918501611c2c565b908501526101608083015184811161015d5782611ed6918501611cc1565b90850152610180928383015190811161015d57611ef39201611cc1565b9082015290565b6040513d6000823e3d90fd5b6001600160a01b031660009081527fade46bbfcf6f898a43d541e42556d456ca0bf9b326df8debc0f29d3f811a03026020526040902090565b6001600160a01b031660009081527fade46bbfcf6f898a43d541e42556d456ca0bf9b326df8debc0f29d3f811a03056020526040902090565b634e487b7160e01b600052601160045260246000fd5b9060018201809211611f9c57565b611f78565b8151916001600160401b03831161125d57600160401b831161125d578154838355808410611fff575b50602080910191600052806000209060005b848110611fea575050505050565b835160581c8382015592810192600101611fdc565b6000838152846020822092830192015b82811061201d575050611fca565b81815560010161200f565b80548210156114de5760005260206000200190600090565b906120df57815181546001600160a01b0319166001600160a01b03919091161781556101af916120c59060809060208101518454604080840151606085015165ffffffffffff60a01b199093169390911c63ffffffff60a01b169290921760c09290921b60ff60c01b169190911790151560c81b60ff60c81b16178455015160ff1690565b815460ff60d01b191660d09190911b60ff60d01b16179055565b634e487b7160e01b600052600060045260246000fd5b61212e61048b949360a093600180861b03168352602083019060ff60408092828151168552602081015115156020860152015116910152565b81608082015201906102b4565b60405190612148826112c7565b60008252565b3d15612179573d9061215f826117b9565b9161216d60405193846112e2565b82523d6000602084013e565b606090565b6001600160a01b03909116815260406020820181905261048b929101906102b4565b909160609282526121bb6020918483850152848401906110da565b604092838183039101528451928382528282019083808660051b8501019701956000935b8685106121f157505050505050505090565b90919293949596978680612267600193601f198682030189528c518760c091878060a01b03815116845263ffffffff60e01b8682015116868501526122578b8201518c86019060ff60408092828151168552602081015115156020860152015116910152565b0151918160a082015201906102b4565b9a0195019501939695949291906121df565b949390929161229761067d6001600160601b03198860601b1661585c565b612c34576122a761067d87612c55565b612c1357604051630c77631360e41b81526000816004816001600160a01b038b165afa908115610b6957600091612bf8575b506122e761067d868361338f565b612be6578351602082019081515103612bb157845160005b818110612b405750505084600161231589611f06565b015561232c8460026123268a611f06565b01611fa1565b60a0810151612b20575b604081018051519060005b828110612b0057505050606081018051519060005b828110612ae0575050506080810151156129fc5761238361237688611f06565b805460ff19166001179055565b959493929190955b815180600461239989611f06565b015560005b81811061285b57505060e08101515160005b8181106128145750506101008101515160005b8181106127c45750506101208101515160005b81811061277c5750506101408101515160005b81811061272c5750506101608101515160005b8181106126cf5750506101808101515160005b81811061264d5750508051519060005b828110612606575050508051956000965b80881061250d57509495509293919290916001600160a01b0386163b1561015d57600061247191604051809381926306d61fe760e41b83526004830161047a565b0381836001600160a01b038b165af190816124f4575b506124b2578461249561214e565b60405163e838e76160e01b8152918291610600916004840161217e565b936124ef7ffd771bb87e415bdacaa78caa524a7e993befbcdda24e89f4da861ecd9410c05993949560405193849360018060a01b031696846121a0565b0390a2565b8061250161250792611298565b8061068f565b38612487565b61251788846114ca565b51805190989061253d90612531906001600160a01b031681565b6001600160a01b031690565b60408a0151606061254e84886114ca565b510151823b1561015d5761257e92600092838d60405196879586948593630fe8486760e31b8552600485016120f5565b03925af190816125f3575b506125c457886125a861259a61214e565b91516001600160a01b031690565b61060060405192839263a8a545cd60e01b84526004840161217e565b60019192939495969798506125d7611939565b60606125e383886114ca565b5101520196959493929190612430565b8061250161260092611298565b38612589565b8061263461262f612621600194869d98999a9b9c9d516114ca565b516001600160e01b03191690565b611443565b61263e8154611f8e565b9055019796959493929761241f565b80610180849a95969798999a015190612665916114ca565b51516001600160e01b0319168980888461018088015190612685916114ca565b51602001519161269492613612565b818985610180890151906126a7916114ca565b5160400151916126b6926134e5565b916126c093612fe5565b6001019796959493929761240f565b8061271f896126ec600194610160889e999a9b9c9d9e01516114ca565b518051612719908b906001600160e01b03191693604061271183836020880151613612565b9401516134e5565b91612f3d565b01979695949392976123fc565b8061276f612748600193610140879d98999a9b9c9d01516114ca565b51612769898d6020612761855163ffffffff60e01b1690565b940151613612565b90613176565b01979695949392976123e9565b806127b7612798600193610120879d98999a9b9c9d01516114ca565b516127b1898d6020612761855163ffffffff60e01b1690565b90613116565b01979695949392976123d6565b806128076127e0600193610100879d98999a9b9c9d01516114ca565b51612801898d60206127f9855163ffffffff60e01b1690565b940151613590565b90612edb565b01979695949392976123c3565b8061284e61282f60019360e0879d98999a9b9c9d01516114ca565b51612848898d6020612711855163ffffffff60e01b1690565b90612e79565b01979695949392976123b0565b61286b8185969798999a956114ca565b5180516001600160a01b031660208201516001600160e01b0319166040830151805160ff166020820151151560409092015160ff16926128a9611310565b6001600160a01b0390951685526001600160e01b031916602085015260ff1660408401521515606083015260ff166080820152826128e68c611f06565b600401906128f391612028565b6128fd9291612040565b80516001600160a01b031661291190611f06565b600301805461291f90611f8e565b9055805160601b6001600160601b0319166000908152600080516020615d6a8339815191526020526040902054156129d9576020810151600192916129c2916001600160e01b03191681519091908d906040906001600160a01b031692015161299261298c825160ff1690565b846157f9565b926129a06020830151151590565b156129cf576129b660406129bc93015160ff1690565b906157f9565b92612fe5565b019796959493929761239e565b5050600092612fe5565b51604051639ca2818b60e01b81526001600160a01b039091166004820152602490fd5b60c08101515160005b818110612a1a5750509594939291909561238b565b612a288160c08501516114ca565b51612a5b612a3e6001600160a01b038c16611f3f565b82516001600160a01b031660009081526020919091526040902090565b805460ff1916600117815590602081015115612a895750805461ff0019166101001790556001905b01612a05565b604001805151916001019060005b838110612aaa5750505050600190612a83565b80612ada612376612ac161262160019587516114ca565b869063ffffffff60e01b16600052602052604060002090565b01612a97565b80612afa8b612af561262160019587516114ca565b612f98565b01612356565b80612b1a8b612b1561262160019587516114ca565b612d99565b01612341565b612b3b612b2c88611f06565b805461ff001916610100179055565b612336565b612b5f612b506115c6838a6114ca565b9060ff8260601c9260581c1690565b509060019182612b6e82611f06565b015415612bc357612b8f61067d612b896126218589516114ca565b83612ce0565b612bb157612b9e600391611f06565b01612ba98154611f8e565b9055016122ff565b604051631794e5f160e01b8152600490fd5b604051639ca2818b60e01b81526001600160a01b03919091166004820152602490fd5b604051635f1b910f60e11b8152600490fd5b612c0d91503d806000833e610b5b81836112e2565b386122d9565b60405163b7038f3960e01b81526001600160a01b0387166004820152602490fd5b604051639d615d0560e01b81526001600160a01b0387166004820152602490fd5b612c5e81612d40565b9081612cce575b81612c6e575090565b60209150600090604051838101906301ffc9a760e01b825263ea15602d60e01b602482015260248152612ca081611242565b5191617530fa6000513d82612cc2575b5081612cbb575b5090565b9050151590565b60201115915038612cb0565b9050612cd981612d69565b1590612c65565b612ce981612d40565b9182612d2e575b82612cfa57505090565b6020925090600091604051848101916301ffc9a760e01b835263ffffffff60e01b16602482015260248152612ca081611242565b9150612d3981612d69565b1591612cf0565b6000602091604051838101906301ffc9a760e01b808352602482015260248152612ca081611242565b6000602091604051838101906301ffc9a760e01b825263ffffffff60e01b602482015260248152612ca081611242565b9190612da48361141d565b80546001600160a01b0316612e3257612dbc8461531c565b612e1057612dc9846154c8565b612dee5780546001600160a01b0319166001600160a01b039092169190911790559050565b604051633cecfc3760e01b81526001600160e01b031985166004820152602490fd5b60405163e171c77960e01b81526001600160e01b031985166004820152602490fd5b60405163ec9cbcb360e01b81526001600160e01b031985166004820152602490fd5b6001600160e01b031990911681526001600160581b0319909116602082015260400190565b612e82826136c5565b6001612e8d8261141d565b01906001600160581b0319825460581b16612ebd575080546001600160a81b03191660589290921c919091179055565b604051632caa037760e21b8152928392506106009160048401612e54565b612ee4826136c5565b6002612eef8261141d565b01906001600160581b0319825460581b16612f1f575080546001600160a81b03191660589290921c919091179055565b60405163e709805160e01b8152928392506106009160048401612e54565b91612f568183612f4c8661141d565b956005870161303f565b6001600160581b0319918216612f82575b16612f6f5750565b805460ff60a81b1916600160a81b179055565b825460ff60a01b1916600160a01b178355612f67565b612fab906001600160401b03199261569c565b166000527fade46bbfcf6f898a43d541e42556d456ca0bf9b326df8debc0f29d3f811a03046020526040600020600160ff19825416179055565b92908183612ff9610246876130039561569c565b956001870161303f565b6001600160581b031991821661302d575b1661301c5750565b805462ff0000191662010000179055565b825461ff001916610100178355613014565b9091906001600160581b0319808316919082156130e05761306361067d84846156bc565b6130c45785169081613078575b505050505050565b61309b9360018261308f61067d9661309695615b61565b5001611544565b6156bc565b6130a85780808080613070565b6106006040519283926320fdf93b60e21b845260048401612e54565b6040516320fdf93b60e21b815280610600868860048401612e54565b8516925090508115613104576130fc91600261067d92016156bc565b6130a8575050565b60405163036488f560e51b8152600490fd5b90613120816136c5565b6131298261141d565b916131416001600160581b03198316600385016156bc565b1561315b57505060010160ff815460a81c1615612f6f5750565b61060060405192839262c3580d60e61b845260048401612e54565b90613180816136c5565b6131898261141d565b916131a16001600160581b03198316600485016156bc565b156131bb57505060020160ff815460a81c1615612f6f5750565b610600604051928392630766bae360e51b845260048401612e54565b90815180825260208080930193019160005b8281106131f7575050505090565b83516001600160e01b031916855293810193928101926001016131e9565b90815180825260208092019182818360051b82019501936000915b8483106132405750505050505090565b909192939495848061328083856001950387528a5190606090868060a01b03835116815284830151151585820152816040809401519382015201906131d7565b9801930193019194939290613230565b6005111561329a57565b634e487b7160e01b600052602160045260246000fd5b8051600581101561329a576040918291845260ff60208201511660208501520151910152565b90815180825260208080930193019160005b8281106132f6575050505090565b9091929382608060019261331e83895163ffffffff60e01b81511684520151848301906132b0565b019501939291016132e8565b90815180825260208080930193019160005b82811061334a575050505090565b909192938260e06001926133836040895163ffffffff851b815116845261337786820151878601906132b0565b015160808301906132b0565b0195019392910161333c565b6040516134de816134d060208201946020865280516133bc6101a0918260408701526101e08601906131d7565b906134bd61340a6133f66133e2602087015195603f1996878b83030160608c01526131d7565b6040870151868a83030160808b01526131d7565b6060860151858983030160a08a01526131d7565b6080850151151560c08801529160a0850151151560e088015261343e60c08601519361010094868a830301868b0152613215565b906134aa61349361347c61346460e08a015195610120968d888c828503019101526132d6565b9689015196610140978c898b828503019101526132d6565b938801519361016094888c830301868d01526132d6565b948701519461018095878b830301878c01526132d6565b918601519085898403019089015261332a565b92015190848303016101c085015261332a565b03601f1981018352826112e2565b5190201490565b91600183516134f381613290565b6134fc81613290565b0361351357506129b6602061048b93015160ff1690565b90506002825161352281613290565b61352b81613290565b03613542576115c690604061048b930151906114ca565b506003815161355081613290565b61355981613290565b0361357057604051635f1b910f60e11b8152600490fd5b6004905161357d81613290565b61358681613290565b14612be657600090565b916001835161359e81613290565b6135a781613290565b036135be57506129b6602061048b93015160ff1690565b9050600282516135cd81613290565b6135d681613290565b036135ed576115c690604061048b930151906114ca565b50600381516135fb81613290565b61360481613290565b036135705750600160581b90565b916001835161362081613290565b61362981613290565b0361364057506129b6602061048b93015160ff1690565b90506002825161364f81613290565b61365881613290565b0361366f576115c690604061048b930151906114ca565b506003815161367d81613290565b61368681613290565b0361369d57604051635f1b910f60e11b8152600490fd5b600490516136aa81613290565b6136b381613290565b146136bd57600090565b600160591b90565b6001600160581b0319161561310457565b906136e08261133c565b6136ed60405191826112e2565b82815280926136fe601f199161133c565b019060005b82811061370f57505050565b806060602080938501015201613703565b6000356001600160e01b0319166137368161141d565b80546001600160a01b03166001600160a01b0381811615613818577f00000000000000000000000000000000000000000000000000000000000000001633141560001461380957613785614c3a565b925b825491606094859260ff8560a01c166137e8575b5090610d5e6137a9926138fd565b936137b2613937565b94156137e05760ff92916137c5916151e4565b60a81c166137d1575090565b6105c2600761048b9201615c94565b845160208601fd5b816137a993929750610d5e94506137fe91614f4a565b93909691925061379b565b6138123661386d565b92613787565b60405163fcfc5aad60e01b81526001600160e01b031985166004820152602490fd5b600091829182602083519301915af190565b908060209392818452848401376000828201840152601f01601f1916010190565b61048b60c460405180936000602083015260006024830152336044830152346064830152608060848301528060a4830152806000848401376000838284010152601f801991011681010360a48101845201826112e2565b9061048b906134d060405193849260006020850152600060248501523360448501523460648501526080608485015260a484019161384c565b8051613931575061390d366117b9565b61391a60405191826112e2565b368152366000602083013760006020368301015290565b60a40190565b604051903d8252601f19603f3d840101166040523d6000602084013e565b903590601e198136030182121561015d57018035906001600160401b03821161015d5760200191813603831361015d57565b6001600160e01b031990358181169392600481106139a457505050565b60040360031b82901b16169150565b9291906139bf84612c55565b6139ed5790600092938392602083519301915af1906139dc61214e565b91156139e457565b50602081519101fd5b604051637d03783760e11b81526001600160a01b0385166004820152602490fd5b91908110156114de5760051b81013590605e198136030182121561015d570190565b6001600160a01b03918216815291166020820152604081019190915260806060820181905261048b9391019161384c565b60405190613a6e8261127d565b600080835260405183613a80826112ab565b60609182815282602082015282604082015282808201528360808201528360a08201528260c08201528260e0820152826101008201528261012082015282610140820152826101608201528261018082015260208201528260408201520152565b9060208282031261015d5781356001600160401b039283821161015d570160608183031261015d5760405192613b1684611242565b813590811161015d57604092613b2d91830161180b565b83526020810135613b3d81611af6565b60208401520135604082015290565b9060405191828154918282526020928383019160005283600020936000905b828210613b81575050506101af925003836112e2565b855460581b6001600160581b03191684526001958601958895509381019390910190613b6b565b600019810191908211611f9c57565b9060405160a081018181106001600160401b0382111761125d57604052608060ff82945460018060a01b038116845263ffffffff60e01b8160401b166020850152818160c01c166040850152818160c81c161515606085015260d01c16910152565b908210156114de576104769160051b810190613955565b600080825560019181838201556002810180549083815581613c89575b5050808260036004930155019081549181815582613c6c575b50505050565b815260208120918201915b82811015613c66578181558301613c77565b83528360208420918201915b828110613ca25750613c4d565b848155018490613c95565b91602061048b93818152019161384c565b9290949394613cf161067d613cec613cdc875160018060a01b031690565b60601b6001600160601b03191690565b6159b5565b61468757602091828501938451613d2861067d60019283613d20613d1b8c5160018060a01b031690565b611f06565b01549061338f565b612be6578651600395908690613d46906001600160a01b0316611f06565b0154614663578751600290613d6f908290613d69906001600160a01b0316611f06565b01613b4c565b80518460005b82811061463457505050610180808a5101515190858b8d876000925b878785106145b5575050509350505050610160858b8487848351015151956000935b878510614558575095505050505050610140858b84838251015151946000925b868410614532575050505050505061012090818a5101515191858c8c6000915b8683106144df575050505050505061010090818951015151918460005b8b8582106144bf5750505050505060e0808851015151908360005b8a84821061447f5793505050505193608094613e4986820151151590565b156143ca57508851613e7190613e67906001600160a01b0316611f06565b805460ff19169055565b8851600497908890613e8b906001600160a01b0316611f06565b0154908460005b8a8d858310614302575050505050506060948589510151518460005b8b8d8a8584106142c9575050505050506040988981510151518560005b8c83821061429a5750505050805151519060005b82811061427457505050899a9b88613f05613d1b879d9b9c9d9b5160018060a01b031690565b01549381151591828061426a575b61425a579594939291906000965b8588106140825750508c51613f4f9650613f4a95506001600160a01b03169350613d1b92505050565b613c30565b8651613f6590612531906001600160a01b031681565b91870151823b1561015d57613f9293600080948951809781968295638a91b0e360e01b84528c8401613cad565b0393f1908161406f575b5061405f5750613faa61214e565b613fb961067d84860151151590565b614039575050815160009261400e925061253191613fdd906001600160a01b031683565b7fd7b55aedaf176e219cf7181e0fd7055f9066797839ebac8eaf0e7891226f83138580a2516001600160a01b031690565b901515907feb7551bad8fd10038dee62a958c2b6f45624499dc800ff8936bb0a4904bdd2fe600080a3565b835161060091906001600160a01b03169351631ad8069f60e21b8152938493840161217e565b9261400e92506125319150611a41565b8061250161407c92611298565b38613f9c565b909192939495969e9d9c8f9b8c8f9e839f819f90898f926140ba6140c091866140b4613d1b875160018060a01b031690565b01612028565b50613bb7565b96888d6140d96125316125318c5160018060a01b031690565b976141438d61413a8d61411c6140f98c8c01519b5160018060a01b031690565b9961411361410a8b85015160ff1690565b9d840151151590565b92015160ff1690565b9261413161412861132f565b60ff909d168d52565b8b019015159052565b60ff1688860152565b8d1561424957614156926103e592613c19565b955b803b1561015d57600095614180879351988997889687946333b61ee160e11b865285016120f5565b0393f19081614236575b5061422957508d6141a861067d8f6141a061214e565b930151151590565b6141ff57508d5190519e9f9d9e9c9d9b9c60009c899290916001600160a01b0390811691167fbbdf38e3835b5c2965aa65f15bb4bef6b629d58bc7eade21276b661b136b3bbd8f80a35b0196959493929190613f21565b81518e918e9161060091906001600160a01b031693516334f2a43f60e11b8152938493840161217e565b9b9c9d9e9f8891506141f2565b8061250161424392611298565b3861418a565b50505061425461213b565b95614158565b8c5163512509d360e11b81528c90fd5b5085811415613f13565b8061428861262f6126218a948651516114ca565b6142928154613ba8565b905501613edf565b6142b1610a22612621846142c194895101516114ca565b80546001600160a01b0319169055565b018690613ecb565b6142fa926142f461262186610246946142eb613e67975160018060a01b031690565b945101516114ca565b9061569c565b018590613eae565b80516143b892614324916140ba9186916140b4906001600160a01b0316611f06565b8051869061433a906001600160a01b0316611f06565b016143458154613ba8565b905580890151614367906001600160e01b03191692516001600160a01b031690565b81518d906001600160a01b031661438b614385604086015160ff1690565b826157f9565b936143996060820151151590565b600090156143c057506143b2926129b691015160ff1690565b926146ec565b018590613e92565b92505050926146ec565b60c08091015151908460005b8c8c8583106143ea57505050505050613e71565b612a3e6144136125316144048689614418965101516114ca565b5194516001600160a01b031690565b611f3f565b805460ff1916815590808801511561443d5750805461ff00191690555b0185906143d6565b6040018051519184019060005b83811061445a5750505050614435565b614473613e67612ac161262184869798999a96516114ca565b0190899493929161444a565b826144a6610a2261449785886144b7965101516114ca565b51516001600160e01b03191690565b0180546001600160a81b0319169055565b018490613e2b565b846144a6610a2261449785886144d7965101516114ca565b018590613e10565b614522866144f48588614528965101516114ca565b519261451c8c61450b865163ffffffff60e01b1690565b95015191516001600160a01b031690565b90613612565b906147e5565b0186908c8e613df3565b61454d926144f48588614547945101516114ca565b90614821565b018690848e8e613dd3565b6145a99361456d86896145a3945101516114ca565b519261459d604061450b84614589885163ffffffff60e01b1690565b9888015185516001600160a01b031661451c565b906134e5565b916146aa565b01869084878f8f613db3565b6143b2816146299561459d60406146188a8d61460f8a996145dd61449785858e5101516114ca565b9c6145fc85856145f38c5160018060a01b031690565b9e5101516114ca565b51015188516001600160a01b031661451c565b995101516114ca565b51015191516001600160a01b031690565b0186908d878e613d91565b8a61464e614648612b506115c685896114ca565b50611f06565b016146598154613ba8565b9055018590613d75565b8751604051632b4ecd8d60e21b81526001600160a01b039091166004820152602490fd5b83516040516271c02f60e31b81526001600160a01b039091166004820152602490fd5b916146b86146c2929361141d565b926005840161472f565b906146dc575b6146cf5750565b805460ff60a81b19169055565b815460ff60a01b191682556146c8565b6102466146fe9161470894959361569c565b926001840161472f565b90614721575b6147155750565b805462ff000019169055565b815461ff001916825561470e565b600093849390926001600160581b03198082169283156147be576147538486615737565b5061475d85615bd5565b6147b5575b168061476e5750505050565b614798916147939161478d60018701916147888484611544565b615737565b50611544565b615bd5565b6147a4575b8080613c66565b6147ad91615b9f565b50388061479d565b60019750614762565b6147d99460020193506147d392501682615737565b50615bd5565b6147df57565b60019150565b6147f161480a9161141d565b916147d360038401916001600160581b03191682615737565b6148115750565b600101805460ff60a81b19169055565b61482d6148469161141d565b916147d360048401916001600160581b03191682615737565b61484d5750565b600201805460ff60a81b19169055565b6001600160e01b0319818116908114614892576301ffc9a760e01b1461488c5761488690611443565b54151590565b50600190565b5050600090565b6040516148a5816112c7565b600081527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036148f0575b610476906000356001600160e01b031916614f4a565b506104766148fc614c3a565b90506148da565b9061490d916151e4565b6101af61492a60076105bc6000356001600160e01b03191661141d565b60405190614937826112c7565b600082526151e4565b60009390926001600160581b0319831615614a9c579061495f91614b15565b916000946149b4575b506020820180516001600160e01b031663af87348360e01b1790526149a49161499f9161499490612b50565b60ff16602483015291565b614abe565b9115612cb7579061048b916155e2565b6149c59194506105bc60039161141d565b928351936000905b8582106149da5750614968565b9093614a026149ec6115c687856114ca565b600160591b6001600160581b0319909116111590565b614a8a57614a16612b506115c687856114ca565b60ff811660248701529091614a2b8387614abe565b6001939092906001600160a01b038416858111614a575750505090614a4f9161553c565b9401906149cd565b60405163332a984f60e11b81526001600160a01b03928316600482015260ff939093166024840152166044820152606490fd5b604051636756835b60e11b8152600490fd5b604051636596ccb160e01b81526001600160e01b031985166004820152602490fd5b80516020926000928401908390600019f1601f3d111615614adf5760005190565b600190565b9035601e198236030181121561015d5701602081359101916001600160401b03821161015d57813603831361015d57565b604051632238633960e21b602082015260006024820152606060448201529161048b91839190614c2690614b5c60848501614b4f836101a4565b6001600160a01b03169052565b602081013560a4850152614b89614b766040830183614ae4565b61016060c48801526101e487019161384c565b614c16614c0a614bb3614b9f6060860186614ae4565b6083198a8703810160e48c0152959161384c565b608085013561010489015260a085013561012489015260c085013561014489015260e0850135610164890152610100850135610184890152614bf9610120860186614ae4565b90858a8403016101a48b015261384c565b92610140810190614ae4565b91868403016101c487015261384c565b90606483015203601f1981018352826112e2565b6000356001600160e01b03191690614c566002610c3f8461141d565b91614c706002614c658361141d565b015460a81c60ff1690565b92614c7a3661386d565b93614d3c575b6001600160581b0319811690600160591b8211614cfe57501580614cca575b614ca65750565b604051637214fb8b60e11b81526001600160e01b0319919091166004820152602490fd5b506356b07d1360e11b8114801590614ced575b8015614c9f575030331415614c9f565b5063278f794360e11b811415614cdd565b6020850180516001600160e01b031663bfd151c160e01b1790526101af9250614d369150614d2b90612b50565b60ff16602486015290565b83614e38565b6020840180516001600160e01b031663031fb36160e21b1790526004614d65816105bc8561141d565b9081519160005b838110614d7c5750505050614c80565b614d896115c682846114ca565b600160591b6001600160581b031982161115614dd25790614dcc614dc6614dbb6001949060ff8260601c9260581c1690565b60ff1660248d015290565b8a614de3565b01614d6c565b604051636756835b60e11b81528490fd5b60008082516020840182865af115614df9575050565b6024604051916084601f19601f3d011601936340b788e360e01b8452600484015201516024820152606060448201523d60648201523d6000608483013efd5b60008082516020840182865af115614e4e575050565b6024604051916084601f19601f3d011601936310b2d36b60e01b8452600484015201516024820152606060448201523d60648201523d6000608483013efd5b60008082516020840182865af115614ea3575050565b6024604051916084601f19601f3d01160193630cb6620d60e01b8452600484015201516024820152606060448201523d60648201523d6000608483013efd5b6338997b1160e01b600052600080516020615d4a833981519152602052610476907f366b46b479d417a249e7f56f296f035e13c924e69b7ed63bca6e286fe8e383b77f366b46b479d417a249e7f56f296f035e13c924e69b7ed63bca6e286fe8e383b6614fdc565b90614f576104769261141d565b6005600682019101614fdc565b90614f716104769261147b565b6001600282019101614fdc565b60208183031261015d578051906001600160401b03821161015d570181601f8201121561015d578051614fb0816117b9565b92614fbe60405194856112e2565b8184526020828401011161015d5761048b9160208085019101610291565b929091614fe884615c94565b90815190600095865b83811061517e575061500b61500588611951565b976136d6565b9560009484156130705783511561516d575b6020840180516001600160e01b031663236b075960e11b179052855b85811061504d575050505050508084528252565b61505a6115c682846114ca565b6001600160581b0319811690600160591b821115614a8a576150b66150979261508c839060ff8260601c9260581c1690565b60ff1660248b015293565b6004918291899061ffff191660005260205261fffc6040600020541690565b160361515a57906150cb61161d8d9387611544565b8051908c6000945b8386106150ea575050505050506001905b01615039565b9b6151489161511d879e61510e848f6115c68b9c61168a849b9c9d6116bb94614e8d565b906001600160581b0319169052565b615138615128613937565b8051602080918301019101614f7e565b61514283836114ca565b526114ca565b5060018091019a019291908c8e6150d3565b509061516860019287614e8d565b6150e4565b92506151783661386d565b9261501d565b9660019061519b6151956116de6115c68c8a6114ca565b846157aa565b019701614ff1565b60409060ff61048b949316815281602082015201906102b4565b60ff61048b949360609360018060a01b0316835216602082015281604082015201906102b4565b908151815115159091825b6151f95750505050565b90919293615215612b506115c661520f87613ba8565b886114ca565b90956001600160a01b0387169491939180156152ce5761523d61523788613ba8565b846114ca565b51965b863b1561015d57604096875190631128186d60e01b825281808960049c8d83019161526a926151a3565b03815a6000948591f190816152bb575b506152a4578888610600898961528e61214e565b9151631996ddff60e21b815294859485016151bd565b9397509095506000199092019350909150826151ef565b806125016152c892611298565b3861527a565b6152d661213b565b96615240565b9190600481106152f05761048b9192613987565b6152fc60249184613987565b60405163fcfc5aad60e01b81526001600160e01b03199091166004820152fd5b63ffffffff60e01b16633a871cdd60e01b81149081156154b7575b81156154a6575b8115615495575b8115615484575b8115615473575b8115615462575b8115615451575b8115615440575b811561542f575b811561541e575b811561540d575b81156153fc575b81156153eb575b81156153da575b81156153c9575b81156153b8575b81156153aa575090565b631d06562b60e11b14919050565b63ceaf130960e01b811491506153a0565b630b0faea160e01b81149150615399565b63190be77560e21b81149150615392565b632344486160e21b8114915061538b565b631cd3c49560e31b81149150615384565b6338997b1160e01b8114915061537d565b6394ed11e760e01b81149150615376565b631a7e6adf60e11b8114915061536f565b635b0e93fb60e11b81149150615368565b63278f794360e11b81149150615361565b6352d1902d60e01b8114915061535a565b6301ffc9a760e01b81149150615353565b631df68add60e21b8114915061534c565b6356b07d1360e11b81149150615345565b631a10fa5160e31b8114915061533e565b63586b48ff60e11b81149150615337565b63ffffffff60e01b1663e3563a4f60e01b811490811561552b575b811561551a575b8115615509575b81156154fb575090565b63a9a2340960e01b14919050565b637a32e3bf60e11b811491506154f1565b63275e2d7960e01b811491506154ea565b6364c530cd60e01b811491506154e3565b9065ffffffffffff808360a01c1680156155db575b818360a01c169182156155d3575b82811690821611156155bd575060a01b65ffffffffffff60a01b16915b8160d01c8160d01c106000146155ac576001600160d01b03198216915b60018060a01b0380911691161791171790565b6001600160d01b0319811691615599565b60a01b65ffffffffffff60a01b1692905061557c565b91508161555f565b5080615551565b90600165ffffffffffff808460a01c168015615695575b818460a01c1691821561568d575b8281169082161115615677575060a01b65ffffffffffff60a01b16925b8260d01c8160d01c10600014615666576001600160d01b03198316925b828060a01b0392838093161460001461566057505060015b1691171790565b16615659565b6001600160d01b0319811692615641565b60a01b65ffffffffffff60a01b16939050615624565b915081615607565b50806155f9565b60601b6001600160601b03191660a09190911c63ffffffff60401b161790565b6156d882829061ffff1916600052602052604060002054151590565b1561572d576156fb82829061ffff191660005260205261fffc6040600020541690565b9160ff8360081c1660ff81146157245761ff00600160ff61048b9616920160081b161791615b2c565b50505050600090565b9061048b9161590b565b61575382829061ffff1916600052602052604060002054151590565b156148925761577682829061ffff191660005260205261fffc6040600020541690565b9160ff8360081c16801561579e5761ff0060ff61048b9516916000190160081b161791615b2c565b509061048b9250615a61565b6157c682829061ffff1916600052602052604060002054151590565b156148925760ff6157f061ffff936001939061ffff191660005260205261fffc6040600020541690565b60081c16011690565b60601b6001600160601b03191660589190911b60ff60581b161790565b6001600052600080516020615d6a833981519152602052600080516020615d8a83398151915290565b600052600080516020615d6a833981519152602052604060002090565b61ffff1916801580156158e7575b6158e1576001600052600080516020615d6a833981519152602052600080516020615d8a83398151915254801580156158d7575b156158c057506158ba81614adf926158b4615816565b5561583f565b60019055565b908060026158d192176158b4615816565b55600190565b506001811661589e565b50600090565b5080600052600080516020615d6a833981519152602052604060002054151561586a565b9061ffff1916908115801561599f575b6148925760016000528060205260406000205480158015615995575b1561596a57508181614adf9361595a6158ba946001600052602052604060002090565b5590600052602052604060002090565b9160028117615983836001600052602052604060002090565b55600052602052604060002055600190565b5060018116615937565b508160005280602052604060002054151561591b565b61ffff1916906159c48261583f565b5482158015615a59575b615a525760016000815b15615a28575b506000906159eb8161583f565b549061fffe198083169190878314615a05575050916159d8565b6158b49294969750946158d19561fffc600283969496169316911617179161583f565b60018116159081615a48575b5015615a4057386159de565b506000925050565b9050151538615a34565b5060009150565b5080156159ce565b919061ffff191691615a7d838290600052602052604060002090565b549083158015615b24575b615a405760016000815b15615af9575b50600090615ab0818490600052602052604060002090565b549061fffe198083169190888314615aca57505091615a92565b61595a929594979850956158d19661fffc60028396949616931691161717918390600052602052604060002090565b60018116159081615b1a575b5015615b115738615a98565b50600093505050565b9050151538615b05565b508115615a88565b9061ffff19166000526020526040600020908154908115615b595761fffc169061fffc1916179055600190565b505050600090565b615b7f82829061ffff191660005260205261fffc6040600020541690565b91600480841614615b9757600461048b931791615b2c565b505050600190565b615bbd82829061ffff191660005260205261fffc6040600020541690565b916004831615615b975761fffb61048b931691615b2c565b60016000526020526040600020548015908115615bf0575090565b600191501690565b60016000818152600080516020615d6a833981519152602052600080516020615d8a83398151915254604051939290815b615c3d575b50810160051b83016040528252565b918183161580615c8b575b15615c855761fffe198316908201600581901b86018290529282919060021615615c7c57615c759061583f565b5490615c29565b50600090615c29565b91615c2e565b50821515615c48565b9060006001809381835280602052604083205490604051955b615cc2575b5050810160051b83016040528252565b90928284161580615d20575b15615d1a5761fffe198416908301600581901b87018290529383919060021615615d1157615d06908390600052602052604060002090565b54905b919091615cad565b50600090615d09565b92615cb2565b50831515615cce56feade46bbfcf6f898a43d541e42556d456ca0bf9b326df8debc0f29d3f811a0300ade46bbfcf6f898a43d541e42556d456ca0bf9b326df8debc0f29d3f811a0303ade46bbfcf6f898a43d541e42556d456ca0bf9b326df8debc0f29d3f811a030194cfd67f685607e46dacbf75e6a3a5cc7e0a77a9dde0d0269384d8135edf6083a2646970667358221220de70c98538ceda69f90033400de9bce15a8cd998a0ec494ef0e0d9ecd52ee39c64736f6c634300081500330000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d2789", + "nonce": "0x47", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x80c2f62037e084f594ce6d0be7de0119d62e5d9faefe5f903515d1b76d9fea40", + "transactionType": "CREATE", + "contractName": "MultiOwnerPlugin", + "contractAddress": "0x56bC629F342821FBe91C5273880792dFECBE7920", + "function": null, + "arguments": null, + "transaction": { + "type": "0x02", + "from": "0x7f89ed1f3f0d52d303904101305471bca3cde710", + "gas": "0x25836c", + "value": "0x0", + "data": "0x6101a06040818152346200019f576200001882620001a4565b6012825260208201907126bab63a349027bbb732b91028363ab3b4b760711b928383528151906200004982620001a4565b600582526020820191640312e302e360dc1b928381526200006a83620001c0565b956101209687526200007c8362000393565b93610140948552519020918260e05251902090610100968288524660a05285519260208401927f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f84528785015260608401524660808401523060a084015260a0835260c0830183811060018060401b038211176200018957600593601282819560e0948b52835190206080523060c0526200011781620001a4565b52019081522092610160938452602085516200013381620001a4565b83815201908152209261018093845251936120dc958662000541873960805186505060a05186505060c05186505060e051865050518550505184610a1b01525183610a4401525182611953015251816119780152f35b634e487b7160e01b600052604160045260246000fd5b600080fd5b604081019081106001600160401b038211176200018957604052565b805160209190828110156200025f575090601f825111620001fe5780825192015190808310620001ef57501790565b82600019910360031b1b161790565b90604051809263305a27a960e01b82528060048301528251908160248401526000935b82851062000245575050604492506000838284010152601f80199101168101030190fd5b848101820151868601604401529381019385935062000221565b6001600160401b03811162000189576000928354926001938481811c9116801562000388575b838210146200037457601f81116200033e575b5081601f8411600114620002d757509282939183928694620002cb575b50501b916000199060031b1c191617905560ff90565b015192503880620002b5565b919083601f1981168780528488209488905b8883831062000323575050501062000309575b505050811b01905560ff90565b015160001960f88460031b161c19169055388080620002fc565b858701518855909601959485019487935090810190620002e9565b85805284601f848820920160051c820191601f860160051c015b8281106200036857505062000298565b87815501859062000358565b634e487b7160e01b86526022600452602486fd5b90607f169062000285565b805160209081811015620004215750601f825111620003c05780825192015190808310620001ef57501790565b90604051809263305a27a960e01b82528060048301528251908160248401526000935b82851062000407575050604492506000838284010152601f80199101168101030190fd5b8481018201518686016044015293810193859350620003e3565b9192916001600160401b038111620001895760019182548381811c9116801562000535575b828210146200051f57601f8111620004e6575b5080601f8311600114620004995750819293946000926200048d575b5050600019600383901b1c191690821b17905560ff90565b01519050388062000475565b90601f198316958460005282600020926000905b888210620004ce57505083859697106200030957505050811b01905560ff90565b808785968294968601518155019501930190620004ad565b8360005283601f83600020920160051c820191601f850160051c015b8281106200051257505062000459565b6000815501849062000502565b634e487b7160e01b600052602260045260246000fd5b90607f16906200044656fe608080604052600436101561001357600080fd5b60003560e01c90816301ffc9a71461105f575080630c7ecd8414610bc45780630c976227146110385780631128186d14610fe8578063118a538914610de05780631626ba7e14610d90578063197ea35b14610d685780632f54bf6e14610d445780633956224714610be857806346d60eb214610bc4578063676c3dc214610b0e5780636d61fe7014610b135780637f42433814610b0e57806384b0196e14610a055780638616d61d146109de57806388e18ce4146109bb5780638a91b0e314610932578063af87348314610911578063affe39c1146108d8578063bfd151c11461086a578063c7763130146101595763f582bceb1461011157600080fd5b346101545760403660031901126101545761012a6110cc565b602435906001600160a01b03821682036101545760209161014a916119f7565b6040519015158152f35b600080fd5b3461015457600036600319011261015457610172611d2e565b5061017b611d2e565b60405161018781611217565b6005815260a03660208301378060408301526101aa633956224760e01b91611a23565b5263affe39c160e01b6101c06040830151611a46565b526317aa5fb760e11b6101d66040830151611a56565b526342580cb760e11b6101ec6040830151611a66565b52630b135d3f60e11b6102026040830151611a76565b5260405161020f816111fc565b6001815260016020820152600060408201526040518060e08101106001600160401b0360e0830111176108145760e081016040526006815260005b60c0811061082a57509061039c9160e084015261029260405161026c816111e1565b633956224760e01b81526020810183905260e08501519061028c82611a23565b52611a23565b506102c86040516102a2816111e1565b635b0e93fb60e11b81526020810183905260e0850151906102c282611a46565b52611a46565b506102fe6040516102d8816111e1565b631a7e6adf60e11b81526020810183905260e0850151906102f882611a56565b52611a56565b5061033460405161030e816111e1565b6356b07d1360e11b81526020810183905260e08501519061032e82611a66565b52611a66565b5061036a604051610344816111e1565b631df68add60e21b81526020810183905260e08501519061036482611a76565b52611a76565b5060405190610378826111e1565b63278f794360e11b8252602082015260e08301519061039682611a86565b52611a86565b50604051906103aa826111fc565b6001825260006020830152600060408301526040516103c8816111fc565b60038152600060208201526000604082015260405192836101608101106001600160401b0361016086011117610814576101608401604052600a845260005b61014081106107d4575061054190839461010061061995015261045060405161042f816111e1565b633956224760e01b8152602081018390526101008701519061028c82611a23565b50610481604051610460816111e1565b635b0e93fb60e11b815260208101839052610100870151906102c282611a46565b506104b2604051610491816111e1565b631a7e6adf60e11b815260208101839052610100870151906102f882611a56565b506104e36040516104c2816111e1565b6356b07d1360e11b8152602081018390526101008701519061032e82611a66565b506105146040516104f3816111e1565b631df68add60e21b8152602081018390526101008701519061036482611a76565b5060405190610522826111e1565b63278f794360e11b825260208201526101008501519061039682611a86565b50610578604051610551816111e1565b630b135d3f60e11b8152602081018390526101008501519061057282611a96565b52611a96565b506105af604051610588816111e1565b6317aa5fb760e11b815260208101839052610100850151906105a982611aa6565b52611aa6565b506105e66040516105bf816111e1565b63affe39c160e01b815260208101839052610100850151906105e082611ab7565b52611ab7565b50604051906105f4826111e1565b6342580cb760e11b825260208201526101008301519061061382611ac8565b52611ac8565b5060405180916020825261068661067061065a61064484516101a060208801526101c0870190611419565b6020850151868203601f19016040880152611419565b6040840151858203601f19016060870152611419565b6060830151848203601f19016080860152611419565b6080820151151560a084015260a0820151151560c084015260c082015190601f198482030160e0850152815180825260208201916020808360051b8301019401926000915b83831061077d578780610779896107466106f68b60e0840151601f198783030161010088015261149c565b6107306107176101008501519261012093601f1989830301858a015261149c565b918401519161014092601f19888303018489015261149c565b90830151858203601f190161016087015261149c565b6107646101608301519161018092601f1987830301848801526114f0565b910151838203601f19016101a08501526114f0565b0390f35b919395965091936020806107c1600193601f19868203018752606060408b51878060a01b038151168452858101511515868501520151918160408201520190611419565b97019301930190928796959492936106cb565b6020906040516107e3816111e1565b600081526040516107f3816111fc565b60008152600084820152600060408201528382015282828801015201610407565b634e487b7160e01b600052604160045260246000fd5b602090604051610839816111e1565b60008152604051610849816111fc565b6000815260008482015260006040820152838201528282850101520161024a565b346101545760ff61087a3661110f565b5050509116156000146108c6576001600160a01b03811633141590816108b4575b506108a257005b60405163ea8e4eb560e01b8152600490fd5b6108bf9150336119f7565b158161089b565b60405163d623472560e01b8152600490fd5b34610154576000366003190112610154576107796108fd6108f833611aed565b611c16565b604051918291602083526020830190611164565b3461015457602061092a610924366113d5565b91611c7b565b604051908152f35b34610154576020366003190112610154576004356001600160401b038111610154576109629036906004016110e2565b505061096d3361159a565b60608101906001805b15610995575b61fffe1916825260808120805460009182905590610976565b6001811615806109b2575b61097c57600183526080822060009055005b508015156109a0565b34610154576109c9366113d5565b505060405163d623472560e01b815260049150fd5b346101545760206109f76109f1366112c3565b906118de565b818151910120604051908152f35b3461015457600036600319011261015457610a3f7f00000000000000000000000000000000000000000000000000000000000000006115c1565b610a687f00000000000000000000000000000000000000000000000000000000000000006116c6565b6040516020808201928284106001600160401b03851117610814579181610ac18594610ab3979660405260008452604051978897600f60f81b895260e0858a015260e08901906111a1565b9087820360408901526111a1565b91466060870152336080870152600060a087015285830360c0870152519182815201929160005b828110610af757505050500390f35b835185528695509381019392810192600101610ae8565b611387565b34610154576020366003190112610154576001600160401b0360043581811161015457610b449036906004016110e2565b919091333b15610bb257610b5733611555565b15610ba15782019060208383031261015457823590811161015457610b7c9201611319565b805115610b8f57610b8d9033611dd5565b005b604051639aa6ffc360e01b8152600490fd5b60405162dc149f60e41b8152600490fd5b604051635d3944c960e11b8152600490fd5b3461015457610bd23661110f565b5050505050600460405163d623472560e01b8152fd5b34610154576040366003190112610154576001600160401b0360043581811161015457610c19903690600401611319565b602491823590811161015457610c33903690600401611319565b91610c3d33611555565b610d3257610c4b8233611dd5565b825160005b818110610cd8575050506080610c653361159a565b6001606082015220548015908115610ccd575b50610b8f57610cb97f8102106be6867f3566db7ac13c2a7afdcb3cba87576742016de13819f97e04e691610cc8604051928392604084526040840190611164565b82810360208401523395611164565b0390a2005b600191501683610c78565b610cf96001600160601b0319610cee8388611ad9565b5160601b1633611ed3565b15610d0657600101610c50565b82906001600160a01b0390610d1b9087611ad9565b5160405163298f10e760e11b815291166004820152fd5b6040516321c4e35760e21b8152600490fd5b3461015457602036600319011261015457602061014a610d626110cc565b336119f7565b3461015457610779610d7c6109f1366112c3565b6040519182916020835260208301906111a1565b34610154576040366003190112610154576024356001600160401b03811161015457610dcd610dc560209236906004016112a5565b60043561177b565b6040516001600160e01b03199091168152f35b3461015457600036600319011261015457610df9611da1565b50610e02611da1565b60405190610e0f826111e1565b601282526020917126bab63a349027bbb732b91028363ab3b4b760711b838201528152604051610e3e816111e1565b60058152640312e302e360dc1b8382015282820190815260405190610e62826111e1565b6007825266416c6368656d7960c81b848301526040830191825260405191610e89836111e1565b601083526f04d6f64696679204f776e6572736869760841b8584015260405190610eb2826111e1565b60019384835260005b878110610fbb575090610ef9610f3f926060880194855260405190610edf826111e1565b633956224760e01b82528982015284519061028c82611a23565b50610f2e610f18604051978989525160808a8a015260a08901906111a1565b945194601f1995868983030160408a01526111a1565b9051848783030160608801526111a1565b9051948285830301608086015285519182815281810182808560051b8401019801946000925b858410610f7257888a0389f35b909192939495968580610fa88c8686869f030188526040838d5163ffffffff60e01b8151168452015191818582015201906111a1565b9b99019796959190910193019190610f65565b968093949597604051610fcd816111e1565b60008152606083820152828288010152019694939296610ebb565b346101545760403660031901126101545760043560ff811603610154576024356001600160401b038111610154576110249036906004016110e2565b505060405163d623472560e01b8152600490fd5b34610154576020366003190112610154576107796108fd6108f861105a6110cc565b611aed565b34610154576020366003190112610154576004359063ffffffff60e01b8216809203610154576020916316cc458360e21b81149081156110a1575b5015158152f35b63ea15602d60e01b8114915081156110bb575b508361109a565b6301ffc9a760e01b149050836110b4565b600435906001600160a01b038216820361015457565b9181601f84011215610154578235916001600160401b038311610154576020838186019501011161015457565b9060806003198301126101545760043560ff8116810361015457916024356001600160a01b0381168103610154579160443591606435906001600160401b03821161015457611160916004016110e2565b9091565b90815180825260208080930193019160005b828110611184575050505090565b83516001600160a01b031685529381019392810192600101611176565b919082519283825260005b8481106111cd575050826000602080949584010152601f8019910116010190565b6020818301810151848301820152016111ac565b604081019081106001600160401b0382111761081457604052565b606081019081106001600160401b0382111761081457604052565b60c081019081106001600160401b0382111761081457604052565b90601f801991011681019081106001600160401b0382111761081457604052565b6001600160401b03811161081457601f01601f191660200190565b92919261127a82611253565b916112886040519384611232565b829481845281830111610154578281602093846000960137010152565b9080601f83011215610154578160206112c09335910161126e565b90565b906040600319830112610154576004356001600160a01b03811681036101545791602435906001600160401b038211610154576112c0916004016112a5565b6001600160401b0381116108145760051b60200190565b9080601f8301121561015457602090823561133381611302565b936113416040519586611232565b818552838086019260051b820101928311610154578301905b828210611368575050505090565b81356001600160a01b038116810361015457815290830190830161135a565b346101545760a0366003190112610154576004356001600160a01b03811603610154576060366023190112610154576084356001600160401b03811161015457610b8d9036906004016110e2565b906003196060818401126101545760043560ff811681036101545792602435916001600160401b038311610154578261016092030112610154576004019060443590565b90815180825260208080930193019160005b828110611439575050505090565b83516001600160e01b0319168552938101939281019260010161142b565b6005111561146157565b634e487b7160e01b600052602160045260246000fd5b60408091805161148681611457565b845260ff60208201511660208501520151910152565b90815180825260208080930193019160005b8281106114bc575050505090565b909192938260806001926114e483895163ffffffff60e01b8151168452015184830190611477565b019501939291016114ae565b90815180825260208080930193019160005b828110611510575050505090565b909192938260e06001926115496040895163ffffffff851b815116845261153d8682015187860190611477565b01516080830190611477565b01950193929101611502565b604080516001600160a01b039092168252639cc6c92360e01b60208301526002908201526001606082015260809020548015908115611592575090565b600191501690565b604080516001600160a01b039092168252639cc6c92360e01b602083015260029082015290565b60ff81146115ff5760ff811690601f82116115ed57604051916115e3836111e1565b8252602082015290565b604051632cd44ac360e21b8152600490fd5b506040516000805490600182811c908084169384156116bc575b60209485841081146116a85783875286949392918115611688575060011461164a575b50506112c092500382611232565b600080805285812095935091905b8183106116705750506112c09350820101388061163c565b85548784018501529485019486945091830191611658565b9150506112c094925060ff191682840152151560051b820101388061163c565b634e487b7160e01b85526022600452602485fd5b91607f1691611619565b60ff81146116e85760ff811690601f82116115ed57604051916115e3836111e1565b50604051600060019081549182811c90808416938415611771575b60209485841081146116a8578387528694939291811561168857506001146117335750506112c092500382611232565b600081815285812095935091905b8183106117595750506112c09350820101388061163c565b85548784018501529485019486945091830191611741565b91607f1691611703565b61179b9060405190602082015260208152611795816111e1565b336118de565b60208151910120906117ad8183611825565b6117b981949294611457565b6117ff5750506080906117cb3361159a565b606091821b6001600160601b031916918101919091522054156117f357630b135d3f60e11b90565b6001600160e01b031990565b61180a925033611fb0565b61181a576001600160e01b031990565b630b135d3f60e11b90565b90604181511460001461184f57611160916020820151906060604084015193015160001a90611859565b5050600090600290565b9291906fa2a8918ca85bafe22016d0b997e4df60600160ff1b0383116118d25791608094939160ff602094604051948552168484015260408301526060820152600093849182805260015afa156118c55781516001600160a01b038116156118bf579190565b50600190565b50604051903d90823e3d90fd5b50505050600090600390565b9060208151910120604090815160208101917fa856bbdae1f2c6e4aa17a75ad7cc5650f184ec4b549174dd7258c9701d663fc6835283820152828152611923816111fc565b51902091815160208101917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f83527f0000000000000000000000000000000000000000000000000000000000000000848301527f0000000000000000000000000000000000000000000000000000000000000000606083015246608083015260018060a01b031660a082015260a081526119bc81611217565b5190209181519261190160f01b6020850152602284015260428301526042825260808201908282106001600160401b03831117610814575290565b90611a0360809261159a565b906bffffffffffffffffffffffff199060601b1660608201522054151590565b805115611a305760200190565b634e487b7160e01b600052603260045260246000fd5b805160011015611a305760400190565b805160021015611a305760600190565b805160031015611a305760800190565b805160041015611a305760a00190565b805160051015611a305760c00190565b805160061015611a305760e00190565b805160071015611a30576101000190565b805160081015611a30576101200190565b805160091015611a30576101400190565b8051821015611a305760209160051b010190565b60018060a01b031690604051828152639cc6c92360e01b92836020830152600291826040820152606081019160019383858095526080938585822054926000975b611bc5575b50505050611b4084611302565b96611b4e6040519889611232565b848852601f19611b5d86611302565b013660208a0137878515611bbb5750604051928352602083015260408201528360009060608301905b858310611b965750505050505050565b8152858484205461ffff198116611bad858c611ad9565b5292019161fffe1916611b86565b9750505050505050565b909192968288161580611c0d575b15611c045782019661fffe19811690851615611bfb57815285832054915b9081939293611b2e565b50600091611bf1565b96829150611b33565b50871515611bd3565b805160005b818110611c2757505090565b80611c3460019285611ad9565b5160601c611c428286611ad9565b5201611c1b565b903590601e198136030182121561015457018035906001600160401b0382116101545760200191813603831361015457565b91907f19457468657265756d205369676e6564204d6573736167653a0a33320000000060005281601c52603c6000209060ff6001611cd2610140840194611ccc611cc58787611c49565b369161126e565b90611825565b9290961614611ced5760405163d623472560e01b8152600490fd5b611cf681611457565b611d1557505050611d0790336119f7565b611d1057600190565b600090565b611d079350611d2791611cc591611c49565b9033611fb0565b604051906101a082018281106001600160401b038211176108145760405281610180606091828152826020820152826040820152828082015260006080820152600060a08201528260c08201528260e0820152826101008201528261012082015282610140820152826101608201520152565b60405190608082018281106001600160401b0382111761081457604052606080838181528160208201528160408201520152565b9080519160005b838110611de95750505050565b611e0a6001600160601b0319611dff8386611ad9565b5160601b1683611e44565b15611e1757600101611ddc565b6024906001600160a01b0390611e2d9085611ad9565b5160405163b20f76e360e01b815291166004820152fd5b9061ffff1916908115611ecc57604080516001600160a01b039092168252639cc6c92360e01b602083015260029082015260608101828152608082208054909290611ec357600160809252209182549283158015611eb9575b15611ead57600193505555600190565b90600217905555600190565b5060018416611e9d565b50505050600090565b5050600090565b919061ffff1916916040519060018060a01b03168152639cc6c92360e01b6020820152600280604083015260608201918483526080918282209283549287158015611fa8575b611f9c5760016000815b15611f6d575b508652818120805460009161fffe1980831691908c8314611f4c57505091611f23565b9550979850509480949198995061fffc925016931691161717905555600190565b60018116159081611f92575b5015611f855738611f29565b5060009750505050505050565b9050151538611f79565b50600096505050505050565b508315611f19565b6108f8611fbf91939293611aed565b9160005b8351811015611ec3576001600160a01b03611fde8286611ad9565b5116604090815190600080602092838501630b135d3f60e11b95868252896024820152876044820152612027816120198d60648301906111a1565b03601f198101835282611232565b51915afa923d1561209e573d61204861203f82611253565b92519283611232565b81523d60008383013e5b83612091575b83612076575b50505061206d57600101611fc3565b50505050600190565b9080929350818051810103126101545701511438808061205e565b9250808351101592612058565b50606061205256fea264697066735822122014abfc7a6de52a87457656e91b424fb7346335ace02ab4b337987018998b66b464736f6c63430008150033", + "nonce": "0x48", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x0f13a3b7e8528a2fb6f1b4ceb43330feab35d56a3ffc7ffc0cbe8bd16ea4d7ba", + "transactionType": "CREATE", + "contractName": "TokenReceiverPlugin", + "contractAddress": "0xa81C0AEaB22b21b4da8d8728063f6570384b48C9", + "function": null, + "arguments": null, + "transaction": { + "type": "0x02", + "from": "0x7f89ed1f3f0d52d303904101305471bca3cde710", + "gas": "0xfb789", + "value": "0x0", + "data": "0x6080806040523461001657610d5b908161001c8239f35b600080fdfe608060408181526004918236101561001657600080fd5b600090813560e01c90816223de29146107f857816301ffc9a7146107a0575080630c7ecd84146104ea5780631128186d14610755578063118a5389146105fd578063150b7a02146105a857806346d60eb214610586578063676c3dc2146105815780636d61fe701461057c5780637f4243381461058157806388e18ce4146105775780638a91b0e31461057c578063af87348314610577578063bc197c81146104ef578063bfd151c1146104ea578063c7763130146101355763f23a6e61146100de57600080fd5b346101325760a0366003190112610132576100f7610870565b5061010061088b565b50608435906001600160401b0382116101325750602092610123913691016108a1565b50505163f23a6e6160e01b8152f35b80fd5b5090346104e657816003193601126104e65761014f610c2b565b50610158610c2b565b90610161610c0c565b8481526101888360806020813682870137828601858152946223de2960e01b948591610c9e565b52630a85bd0160e11b918261019d8751610cc1565b5263f23a6e6160e01b94856101b28851610cd1565b5263bc197c8160e01b806101c68951610ce1565b526101cf610bb8565b600381528b858201528b8a8201526101e5610c0c565b978d89528c5b8581106104a05750906102736102959392610100809a019a8b5261022a610210610bed565b878152848a8201528c519061022482610c9e565b52610c9e565b50610250610236610bed565b8a8152848a8201528c519061024a82610cc1565b52610cc1565b50610259610bed565b90815282888201528a519061026d82610cd1565b52610cd1565b5061027c610bed565b9182528582015287519061028f82610ce1565b52610ce1565b508751938285018581106001600160401b0382111761048d5789939c50906103459b9492918497969752600387526102d7606097883686830137808d52610c9e565b526102e28a51610cc1565b52630271189760e51b6102f58a51610cd1565b5261035782519a8b98838a526103358c80519b866103206101a09e8f838501526101c0840190610a85565b92015190601f199e8f82850301910152610a85565b90518d82038c01898f0152610a85565b8a8701518c82038b018d8f0152610a85565b9a890151151560a08b015260a0890151151560c08b015260c089015194888b8d030160e08c01528b8651938482528380808401938760051b0101980195925b85841061043c578d806104388f8f8f8f8f8f6103f96103e26103cd61042794610410948c60e08c0151918b82850301910152610aff565b94519461012095888c830301878d0152610aff565b938701519361014094878b830301868c0152610aff565b928601519261016093868a830301858b0152610aff565b9185015191610180928589830301848a0152610b53565b930151918584030190850152610b53565b0390f35b90919293949597858f61047c838f878e8892879660019903018b5251878060a01b0381511684528581015115158685015201519181898201520190610a85565b9a0194019401929594939190610396565b634e487b7160e01b8c5260418d5260248cfd5b869798508d8c6104b598939495969798610bed565b918083526104c1610bb8565b9181835281868401528201528382015282828c01015201908b979695949392916101eb565b5080fd5b610923565b50346101325760a036600319011261013257610509610870565b5061051261088b565b506001600160401b03906044358281116104e6576105339036908601610a55565b50506064358281116104e65761054c9036908601610a55565b50506084359182116101325750602092610568913691016108a1565b50505163bc197c8160e01b8152f35b610a07565b6109d7565b610987565b50346101325750610596366108ce565b50505050505163d623472560e01b8152fd5b5034610132576080366003190112610132576105c2610870565b506105cb61088b565b50606435906001600160401b03821161013257506020926105ee913691016108a1565b505051630a85bd0160e11b8152f35b5034610132578060031936011261013257610616610cf1565b5061061f610cf1565b91610628610bed565b9160158352602092742a37b5b2b7102932b1b2b4bb32b91028363ab3b4b760591b848201528452610657610bed565b9160058352640312e302e360dc1b84840152838501928352610677610bed565b936007855266416c6368656d7960c81b8186015281860194855260606106d98351968388526106c96106b48a516080878c015260a08b0190610947565b975197601f1998898b830301888c0152610947565b90518789830301848a0152610947565b96015193808688030160808701528451928388528288019183808660051b8b0101970195985b858a1061070c5788880389f35b9091929394959685806107428386866001960301895287838d5163ffffffff60e01b815116845201519181858201520190610947565b99019501990198929190959493956106ff565b5034610132578160031936011261013257823560ff81160361013257602435906001600160401b038211610132575061079190369084016108a1565b50505163d623472560e01b8152fd5b905083346107f45760203660031901126107f4573563ffffffff60e01b81168091036107f4576020925063ea15602d60e01b81149081156107e3575b5015158152f35b6301ffc9a760e01b149050836107dc565b8280fd5b8285346104e65760c03660031901126104e657610813610870565b5061081c61088b565b506044356001600160a01b038116036104e6576001600160401b039060843582811161086c5761084f90369083016108a1565b505060a4359182116107f457610867913691016108a1565b505080f35b8380fd5b600435906001600160a01b038216820361088657565b600080fd5b602435906001600160a01b038216820361088657565b9181601f84011215610886578235916001600160401b038311610886576020838186019501011161088657565b9060806003198301126108865760043560ff8116810361088657916024356001600160a01b0381168103610886579160443591606435906001600160401b0382116108865761091f916004016108a1565b9091565b3461088657610931366108ce565b5050505050600460405163d623472560e01b8152fd5b919082519283825260005b848110610973575050826000602080949584010152601f8019910116010190565b602081830181015184830182015201610952565b346108865760a0366003190112610886576004356001600160a01b03811603610886576060366023190112610886576084356001600160401b038111610886576109d59036906004016108a1565b005b34610886576020366003190112610886576004356001600160401b038111610886576109d59036906004016108a1565b34610886576003196060368201126108865760043560ff81160361088657602435906001600160401b0382116108865761016091360301126108865760405163d623472560e01b8152600490fd5b9181601f84011215610886578235916001600160401b038311610886576020808501948460051b01011161088657565b90815180825260208080930193019160005b828110610aa5575050505090565b83516001600160e01b03191685529381019392810192600101610a97565b80516005811015610ae9576040918291845260ff60208201511660208501520151910152565b634e487b7160e01b600052602160045260246000fd5b90815180825260208080930193019160005b828110610b1f575050505090565b90919293826080600192610b4783895163ffffffff60e01b8151168452015184830190610ac3565b01950193929101610b11565b90815180825260208080930193019160005b828110610b73575050505090565b909192938260e0600192610bac6040895163ffffffff851b8151168452610ba08682015187860190610ac3565b01516080830190610ac3565b01950193929101610b65565b60405190606082018281106001600160401b03821117610bd757604052565b634e487b7160e01b600052604160045260246000fd5b60405190604082018281106001600160401b03821117610bd757604052565b6040519060a082018281106001600160401b03821117610bd757604052565b604051906101a082018281106001600160401b03821117610bd75760405281610180606091828152826020820152826040820152828082015260006080820152600060a08201528260c08201528260e0820152826101008201528261012082015282610140820152826101608201520152565b805115610cab5760200190565b634e487b7160e01b600052603260045260246000fd5b805160011015610cab5760400190565b805160021015610cab5760600190565b805160031015610cab5760800190565b60405190608082018281106001600160401b03821117610bd75760405260608083818152816020820152816040820152015256fea2646970667358221220280e8925b6d1a8dd02f2ebd2820b719889039bd6946c72d5e49ae8e6fb24505364736f6c63430008150033", + "nonce": "0x49", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xcefba29b52d19365a4b859228052e7415eed0576786ea4330dbe977cf2dbf7cc", + "transactionType": "CREATE", + "contractName": "MultiOwnerMSCAFactory", + "contractAddress": "0xFD14c78640d72f73CC88238E2f7Df3273Ee84043", + "function": null, + "arguments": [ + "0x7f89Ed1F3F0d52d303904101305471bca3cde710", + "0x56bC629F342821FBe91C5273880792dFECBE7920", + "0xb2b748c2557c552B8636862E41aB3649319dD045", + "0x220f6919637b5bebbfca862c1c942381e30468578a1a80f174db1664282c1ab2", + "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" + ], + "transaction": { + "type": "0x02", + "from": "0x7f89ed1f3f0d52d303904101305471bca3cde710", + "gas": "0x1642a8", + "value": "0x0", + "data": "0x610100346100f657601f61144138819003918201601f19168301916001600160401b038311848410176100fb5780849260a0946040528339810103126100f65761004881610111565b9061005560208201610111565b61006160408301610111565b90608060608401519301519360018060a01b03851685036100f65761008e9061008933610125565b610125565b60805260a05260c05260e0526040516112d4908161016d823960805181818161070301526108ec015260a05181818161030a015281816107f60152610b88015260c05181610934015260e05181818160e40152818161024c015281816105df015261065d0152f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b03821682036100f657565b600080546001600160a01b039283166001600160a01b03198216811783559216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a356fe60808060405260043610156200001f575b5036156200001d57600080fd5b005b600090813560e01c90816356973ee51462000b7257508063715018a61462000b1457806373876732146200073257806381785dfd14620006eb5780638da5cb5b14620006c2578063bb9fe6bf146200063d578063c23a5cea14620005ac578063d9caed1214620003a9578063e189e379146200027b578063e8eb3cc61462000234578063f2fde38b146200016a5763fbb1c3d4036200001057604036600319011262000155578060043563ffffffff81168091036200016757620000e262000c27565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690813b1562000163578290602460405180948193621cb65b60e51b835260048301528235905af180156200015857620001425750f35b6200014d9062000c80565b620001555780f35b80fd5b6040513d84823e3d90fd5b5050fd5b50fd5b50346200015557602036600319011262000155576200018862000c10565b6200019262000c27565b6001600160a01b03908116908115620001e0576000548260018060a01b0319821617600055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a380f35b60405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608490fd5b503462000155578060031936011262000155576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50346200015557620002cb62000397620002af620002993662000bbb565b6040516020969394909283929088840162000cd5565b0392620002c5601f199485810184528362000cb2565b62000e5a565b90620003806200038d856104169360405190620002eb8387018362000cb2565b8582528282019562000e898739604051620003568482019282620003497f00000000000000000000000000000000000000000000000000000000000000008660609160018060a01b0316815260406020820152600060408201520190565b0390810183528262000cb2565b60405195869362000370868601998a925192839162000d4b565b8401915180938684019062000d4b565b0103808452018262000cb2565b5190209062000d97565b6040516001600160a01b039091168152f35b5034620001555760603660031901126200015557620003c762000c10565b6001600160a01b03906024358281169190829003620005a7578391620003ec62000c27565b8062000427575081809381924791839183156200041c575b1690f115620004105780f35b604051903d90823e3d90fd5b6108fc925062000404565b91506040516020938482019263a9059cbb60e01b8452166024820152604435604482015260448152608081019167ffffffffffffffff9180841083851117620005825786918291856040526200047d8662000c95565b8786527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460a0820152519082875af13d1562000598573d9182116200058257620004e89360405192620004da87601f19601f840116018562000cb2565b83523d878785013e62000db7565b80518281159182156200055b575b505090501562000504575080f35b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152fd5b83809293500103126200057e5781015180151581036200057e57808238620004f6565b8280fd5b634e487b7160e01b600052604160045260246000fd5b620004e8936060925062000db7565b600080fd5b503462000155576020366003190112620001555780620005cb62000c10565b620005d562000c27565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169190823b1562000638576024849283604051958694859363611d2e7560e11b85521660048401525af180156200015857620001425750f35b505050fd5b503462000155578060031936011262000155576200065a62000c27565b807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b156200016757819060046040518094819363bb9fe6bf60e01b83525af180156200015857620006b4575080f35b620006bf9062000c80565b80f35b50346200015557806003193601126200015557546040516001600160a01b039091168152602090f35b503462000155578060031936011262000155576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50346200015557620007443662000bbb565b90929160405191620007568362000c95565b60018352835b6020811062000b02575062000794620007bf939495620007856040519384926020840162000cd5565b03601f19810183528262000cb2565b6200079f8462000d27565b52620007ab8362000d27565b50620007b78362000d27565b519062000e5a565b6040519190610416620007d6602082018562000cb2565b80845262000e8993620008516020820183878239620003806200084760207f000000000000000000000000000000000000000000000000000000000000000095604051828101906200035681620007858b8560609160018060a01b0316815260406020820152600060408201520190565b5190208462000d97565b94853b156200086f575b6040516001600160a01b0387168152602090f35b604051928084019184831067ffffffffffffffff84111762000aee5791849391620008b89385396001600160a01b03909116815260406020820181905260009082015260600190565b039085f515620001585760405190620008d18262000c95565b6001825260208201906020368337620008ea8362000d27565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169052604051620009248162000c95565b60018152602081019060203683377f00000000000000000000000000000000000000000000000000000000000000006200095e8262000d27565b5260405192839160608301906040602085015251809152608083019390895b81811062000ad457505050601f19828403016040830152805180845260208401936020808360051b8301019301948a915b83831062000aa05750505050620009cf925003601f19810183528262000cb2565b6001600160a01b0384163b1562000a9c57918491604051938492631cd3c49560e31b845260448401906040600486015251809152606484019290855b81811062000a7657505050828203600319016024840152829162000a2f9162000d70565b0381836001600160a01b0387165af1801562000a6b576020935062000a59575b808080806200085b565b62000a649062000c80565b3862000a4f565b6040513d85823e3d90fd5b82516001600160a01b031685528996508795506020948501949092019160010162000a0b565b8480fd5b9193600191939550602062000ac18192601f19868203018752895162000d70565b97019301930190928694929593620009ae565b82518652602095860195879550909201916001016200097d565b634e487b7160e01b89526041600452602489fd5b8060606020809387010152016200075c565b5034620001555780600319360112620001555762000b3162000c27565b80546001600160a01b03198116825581906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b90503462000bb7578160031936011262000bb7577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5080fd5b6040600319820112620005a7576004359160243567ffffffffffffffff92838211620005a75780602383011215620005a7578160040135938411620005a75760248460051b83010111620005a7576024019190565b600435906001600160a01b0382168203620005a757565b6000546001600160a01b0316330362000c3c57565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b67ffffffffffffffff81116200058257604052565b6040810190811067ffffffffffffffff8211176200058257604052565b90601f8019910116810190811067ffffffffffffffff8211176200058257604052565b9091604060209282848201858352520192916000805b83821062000cfb57505050505090565b9091929394853560018060a01b0381168091036200057e57815283019483019392916001019062000ceb565b80511562000d355760200190565b634e487b7160e01b600052603260045260246000fd5b60005b83811062000d5f5750506000910152565b818101518382015260200162000d4e565b9060209162000d8b8151809281855285808601910162000d4b565b601f01601f1916010190565b605591600b9160405191604083015260208201523081520160ff81532090565b9192901562000e1c575081511562000dcd575090565b3b1562000dd75790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b82519091501562000e305750805190602001fd5b60405162461bcd60e51b81526020600482015290819062000e5690602483019062000d70565b0390fd5b9062000e82620007859160405192839160208301958652604080840152606083019062000d70565b5190209056fe604060808152610416908138038061001681610218565b93843982019181818403126102135780516001600160a01b038116808203610213576020838101516001600160401b0394919391858211610213570186601f820112156102135780519061007161006c83610253565b610218565b918083528583019886828401011161021357888661008f930161026e565b813b156101b9577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b031916841790556000927fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b8480a28051158015906101b2575b61010b575b855160d190816103458239f35b855194606086019081118682101761019e578697849283926101889952602788527f416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c87890152660819985a5b195960ca1b8a8901525190845af4913d15610194573d9061017a61006c83610253565b91825281943d92013e610291565b508038808080806100fe565b5060609250610291565b634e487b7160e01b84526041600452602484fd5b50826100f9565b855162461bcd60e51b815260048101859052602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608490fd5b600080fd5b6040519190601f01601f191682016001600160401b0381118382101761023d57604052565b634e487b7160e01b600052604160045260246000fd5b6001600160401b03811161023d57601f01601f191660200190565b60005b8381106102815750506000910152565b8181015183820152602001610271565b919290156102f357508151156102a5575090565b3b156102ae5790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b8251909150156103065750805190602001fd5b6044604051809262461bcd60e51b825260206004830152610336815180928160248601526020868601910161026e565b601f01601f19168101030190fdfe608060405236156054577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc54600090819081906001600160a01b0316368280378136915af43d82803e156050573d90f35b3d90fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc54600090819081906001600160a01b0316368280378136915af43d82803e156050573d90f3fea2646970667358221220431fb6d6360be5bcc06b4ceb4b8305301f868185e8dcb3820a2de9d256a3321464736f6c63430008150033a2646970667358221220c509919fb7287783a5c402c342758273c381b377054a31beab5e33b6a5df67b264736f6c634300081500330000000000000000000000007f89ed1f3f0d52d303904101305471bca3cde71000000000000000000000000056bc629f342821fbe91c5273880792dfecbe7920000000000000000000000000b2b748c2557c552b8636862e41ab3649319dd045220f6919637b5bebbfca862c1c942381e30468578a1a80f174db1664282c1ab20000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d2789", + "nonce": "0x4a", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xb2179964a1836d4510adb53395bbf338881895fb1943e077bfffdea583a5fa57", + "transactionType": "CALL", + "contractName": "MultiOwnerMSCAFactory", + "contractAddress": "0xFD14c78640d72f73CC88238E2f7Df3273Ee84043", + "function": "addStake(uint32,uint256)", + "arguments": [ + "86400", + "1" + ], + "transaction": { + "type": "0x02", + "from": "0x7f89ed1f3f0d52d303904101305471bca3cde710", + "to": "0xfd14c78640d72f73cc88238e2f7df3273ee84043", + "gas": "0x1d0da", + "value": "0x1", + "data": "0xfbb1c3d400000000000000000000000000000000000000000000000000000000000151800000000000000000000000000000000000000000000000000000000000000001", + "nonce": "0x4b", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x5cb7347b960e28bd3bf3a4958f438cd477ed9a2bdf2c2862ade7b33ce951b14c", + "transactionType": "CREATE", + "contractName": "MultiOwnerTokenReceiverMSCAFactory", + "contractAddress": "0x22322E35c1850F26DD54Ed8F59a27C1c79847A15", + "function": null, + "arguments": [ + "0x7f89Ed1F3F0d52d303904101305471bca3cde710", + "0x56bC629F342821FBe91C5273880792dFECBE7920", + "0xa81C0AEaB22b21b4da8d8728063f6570384b48C9", + "0xb2b748c2557c552B8636862E41aB3649319dD045", + "0x220f6919637b5bebbfca862c1c942381e30468578a1a80f174db1664282c1ab2", + "0x011886f4265b159d818bf87d48a5e63e27be65fb8150c6badf48b183d2deea9d", + "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" + ], + "transaction": { + "type": "0x02", + "from": "0x7f89ed1f3f0d52d303904101305471bca3cde710", + "gas": "0x177a88", + "value": "0x0", + "data": "0x610140346200013b57601f620015a038819003918201601f19168301916001600160401b03831184841017620001405780849260e0946040528339810103126200013b576200004e8162000156565b6200005c6020830162000156565b6200006a6040840162000156565b620000786060850162000156565b9060808501519260c060a08701519601519460018060a01b03861686036200013b57620000b090620000aa336200016b565b6200016b565b60805260a05260c05260e052610100918252610120908152604051906113ed9283620001b3843960805183818161078d0152610976015260a05183818161060701526109ad015260c051838181610316015281816108800152610c76015260e051836109f501525182610a2201525181818160f0015281816102580152818161066901526106e70152f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b03821682036200013b57565b600080546001600160a01b039283166001600160a01b03198216811783559216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a356fe60808060405260043610156200001f575b5036156200001d57600080fd5b005b600090813560e01c90816356973ee51462000c6057508063715018a61462000c025780637387673214620007bc57806381785dfd14620007755780638da5cb5b146200074c578063bb9fe6bf14620006c7578063c23a5cea1462000636578063c3ed7eb514620005ef578063d9caed1214620003b5578063e189e3791462000287578063e8eb3cc61462000240578063f2fde38b14620001765763fbb1c3d4036200001057604036600319011262000161578060043563ffffffff81168091036200017357620000ee62000d15565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690813b156200016f578290602460405180948193621cb65b60e51b835260048301528235905af1801562000164576200014e5750f35b620001599062000d6e565b620001615780f35b80fd5b6040513d84823e3d90fd5b5050fd5b50fd5b50346200016157602036600319011262000161576200019462000cfe565b6200019e62000d15565b6001600160a01b03908116908115620001ec576000548260018060a01b0319821617600055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a380f35b60405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608490fd5b503462000161578060031936011262000161576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50346200016157620002d7620003a3620002bb620002a53662000ca9565b6040516020969394909283929088840162000dd9565b0392620002d1601f199485810184528362000db6565b62000f73565b906200038c62000399856104169360405190620002f78387018362000db6565b8582528282019562000fa28739604051620003628482019282620003557f00000000000000000000000000000000000000000000000000000000000000008660609160018060a01b0316815260406020820152600060408201520190565b0390810183528262000db6565b6040519586936200037c868601998a925192839162000e64565b8401915180938684019062000e64565b0103808452018262000db6565b5190209062000eb0565b6040516001600160a01b039091168152f35b5034620001615760603660031901126200016157620003d362000cfe565b6001600160a01b039060248035838116929190839003620005ea578492620003fa62000d15565b806200043657505081809381924791839183156200042b575b1690f1156200041f5780f35b604051903d90823e3d90fd5b6108fc925062000413565b909250604051906020948583019363a9059cbb60e01b85521684830152604435604483015260448252608082019267ffffffffffffffff9280851084861117620005d55760c0810185811085821117620005c0576040528685527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460a082015251879182919082855af1903d15620005af573d9283116200059a57906200050093929160405192620004f288601f19601f840116018562000db6565b83523d888885013e62000ed0565b805183811591821562000573575b50509050156200051d57505080f35b90602a6084926040519262461bcd60e51b845260048401528201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152fd5b83809293500103126200059657820151801515810362000596578083386200050e565b8380fd5b84634e487b7160e01b60005260416004526000fd5b906200050093925060609162000ed0565b86634e487b7160e01b60005260416004526000fd5b85634e487b7160e01b60005260416004526000fd5b600080fd5b503462000161578060031936011262000161576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034620001615760203660031901126200016157806200065562000cfe565b6200065f62000d15565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169190823b15620006c2576024849283604051958694859363611d2e7560e11b85521660048401525af1801562000164576200014e5750f35b505050fd5b50346200016157806003193601126200016157620006e462000d15565b807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b156200017357819060046040518094819363bb9fe6bf60e01b83525af1801562000164576200073e575080f35b620007499062000d6e565b80f35b50346200016157806003193601126200016157546040516001600160a01b039091168152602090f35b503462000161578060031936011262000161576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50346200016157620007ce3662000ca9565b90929160405191620007e08362000d99565b60028352835b6040811062000bf057506200081e620008499394956200080f6040519384926020840162000dd9565b03601f19810183528262000db6565b620008298462000e2f565b52620008358362000e2f565b50620008418362000e2f565b519062000f73565b604051919061041662000860602082018562000db6565b80845262000fa293620008db60208201838782396200038c620008d160207f0000000000000000000000000000000000000000000000000000000000000000956040518281019062000362816200080f8b8560609160018060a01b0316815260406020820152600060408201520190565b5190208462000eb0565b94853b15620008f9575b6040516001600160a01b0387168152602090f35b604051928084019184831067ffffffffffffffff84111762000bdc5791849391620009429385396001600160a01b03909116815260406020820181905260009082015260600190565b039085f5156200016457604051906200095b8262000d99565b6002825260208201906040368337620009748362000e2f565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169052620009ab8362000e53565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169052604051620009e58162000d99565b60028152602081019060403683377f000000000000000000000000000000000000000000000000000000000000000062000a1f8262000e2f565b527f000000000000000000000000000000000000000000000000000000000000000062000a4c8262000e53565b5260405192839160608301906040602085015251809152608083019390895b81811062000bc257505050601f19828403016040830152805180845260208401936020808360051b8301019301948a915b83831062000b8e575050505062000abd925003601f19810183528262000db6565b6001600160a01b0384163b1562000b8a57918491604051938492631cd3c49560e31b845260448401906040600486015251809152606484019290855b81811062000b6457505050828203600319016024840152829162000b1d9162000e89565b0381836001600160a01b0387165af1801562000b59576020935062000b47575b80808080620008e5565b62000b529062000d6e565b3862000b3d565b6040513d85823e3d90fd5b82516001600160a01b031685528996508795506020948501949092019160010162000af9565b8480fd5b9193600191939550602062000baf8192601f19868203018752895162000e89565b9701930193019092869492959362000a9c565b825186526020958601958795509092019160010162000a6b565b634e487b7160e01b89526041600452602489fd5b806060602080938701015201620007e6565b5034620001615780600319360112620001615762000c1f62000d15565b80546001600160a01b03198116825581906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b90503462000ca5578160031936011262000ca5577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5080fd5b6040600319820112620005ea576004359160243567ffffffffffffffff92838211620005ea5780602383011215620005ea578160040135938411620005ea5760248460051b83010111620005ea576024019190565b600435906001600160a01b0382168203620005ea57565b6000546001600160a01b0316330362000d2a57565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b67ffffffffffffffff811162000d8357604052565b634e487b7160e01b600052604160045260246000fd5b6060810190811067ffffffffffffffff82111762000d8357604052565b90601f8019910116810190811067ffffffffffffffff82111762000d8357604052565b9091604060209282848201858352520192916000805b83821062000dff57505050505090565b9091929394853560018060a01b03811680910362000e2b57815283019483019392916001019062000def565b8280fd5b80511562000e3d5760200190565b634e487b7160e01b600052603260045260246000fd5b80516001101562000e3d5760400190565b60005b83811062000e785750506000910152565b818101518382015260200162000e67565b9060209162000ea48151809281855285808601910162000e64565b601f01601f1916010190565b605591600b9160405191604083015260208201523081520160ff81532090565b9192901562000f35575081511562000ee6575090565b3b1562000ef05790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b82519091501562000f495750805190602001fd5b60405162461bcd60e51b81526020600482015290819062000f6f90602483019062000e89565b0390fd5b9062000f9b6200080f9160405192839160208301958652604080840152606083019062000e89565b5190209056fe604060808152610416908138038061001681610218565b93843982019181818403126102135780516001600160a01b038116808203610213576020838101516001600160401b0394919391858211610213570186601f820112156102135780519061007161006c83610253565b610218565b918083528583019886828401011161021357888661008f930161026e565b813b156101b9577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b031916841790556000927fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b8480a28051158015906101b2575b61010b575b855160d190816103458239f35b855194606086019081118682101761019e578697849283926101889952602788527f416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c87890152660819985a5b195960ca1b8a8901525190845af4913d15610194573d9061017a61006c83610253565b91825281943d92013e610291565b508038808080806100fe565b5060609250610291565b634e487b7160e01b84526041600452602484fd5b50826100f9565b855162461bcd60e51b815260048101859052602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608490fd5b600080fd5b6040519190601f01601f191682016001600160401b0381118382101761023d57604052565b634e487b7160e01b600052604160045260246000fd5b6001600160401b03811161023d57601f01601f191660200190565b60005b8381106102815750506000910152565b8181015183820152602001610271565b919290156102f357508151156102a5575090565b3b156102ae5790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b8251909150156103065750805190602001fd5b6044604051809262461bcd60e51b825260206004830152610336815180928160248601526020868601910161026e565b601f01601f19168101030190fdfe608060405236156054577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc54600090819081906001600160a01b0316368280378136915af43d82803e156050573d90f35b3d90fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc54600090819081906001600160a01b0316368280378136915af43d82803e156050573d90f3fea2646970667358221220431fb6d6360be5bcc06b4ceb4b8305301f868185e8dcb3820a2de9d256a3321464736f6c63430008150033a264697066735822122037146aa65f55e811be3ec40b4985aa1c3023160c64d100dd47e2045f78d38bc464736f6c634300081500330000000000000000000000007f89ed1f3f0d52d303904101305471bca3cde71000000000000000000000000056bc629f342821fbe91c5273880792dfecbe7920000000000000000000000000a81c0aeab22b21b4da8d8728063f6570384b48c9000000000000000000000000b2b748c2557c552b8636862e41ab3649319dd045220f6919637b5bebbfca862c1c942381e30468578a1a80f174db1664282c1ab2011886f4265b159d818bf87d48a5e63e27be65fb8150c6badf48b183d2deea9d0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d2789", + "nonce": "0x4c", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x045e474abf86398ebbf1814e8efd5407a1800a52d6d3bc30be6e0e7d2536db54", + "transactionType": "CALL", + "contractName": "MultiOwnerTokenReceiverMSCAFactory", + "contractAddress": "0x22322E35c1850F26DD54Ed8F59a27C1c79847A15", + "function": "addStake(uint32,uint256)", + "arguments": [ + "86400", + "1" + ], + "transaction": { + "type": "0x02", + "from": "0x7f89ed1f3f0d52d303904101305471bca3cde710", + "to": "0x22322e35c1850f26dd54ed8f59a27c1c79847a15", + "gas": "0x1b724", + "value": "0x1", + "data": "0xfbb1c3d400000000000000000000000000000000000000000000000000000000000151800000000000000000000000000000000000000000000000000000000000000001", + "nonce": "0x4d", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x8d523325b3f21dedb6e264a1da4295ef0e00cc59c3d2c26d6dcea079f6b00e47", + "transactionType": "CREATE", + "contractName": "SessionKeyPlugin", + "contractAddress": "0x588dCE36DdeB0e40320AD1d9909a1f86053D7Df1", + "function": null, + "arguments": null, + "transaction": { + "type": "0x02", + "from": "0x7f89ed1f3f0d52d303904101305471bca3cde710", + "gas": "0x205f14", + "value": "0x0", + "data": "0x6080806040523461001657611c8a908161001c8239f35b600080fdfe608080604052600436101561001357600080fd5b60003560e01c90816301ffc9a714610f92575080630b45f4b414610f665780630c7ecd84146107cf5780631128186d14610f16578063118a538914610d0b5780632237570a14610cdd57806331d99c2c14610ab857806336d0b31a14610a9157806346d60eb214610a6d578063676c3dc2146108a15780636d61fe70146108d7578063717493c7146108a65780637f424338146108a157806388e18ce41461087e5780638a91b0e3146107f5578063af873483146107d4578063bfd151c1146107cf578063c77631301461029b578063ca9b97f31461014b5763fd5e71c5146100fb57600080fd5b346101465760203660031901126101465760206080610118610fff565b6101213361169f565b906bffffffffffffffffffffffff199060601b16606082015220541515604051908152f35b600080fd5b34610146576040366003190112610146576001600160401b036004358181116101465761017c903690600401611109565b602492833590808211610146573660238301121561014657816004013590811161014657848201918536918360061b0101116101465760005b81811061022d5750505060005b8181106101cb57005b6101f46001600160601b03196101ea6101e5848688611709565b6113bb565b60601b1633611719565b15610201576001016101c2565b6101e59161020e93611709565b60405163512c6ac960e11b81526001600160a01b039091166004820152fd5b6102616001600160601b03196102476101e58486886116f9565b60601b1660206102588486886116f9565b013590336117a8565b1561026e576001016101b5565b6101e59061027d9287946116f9565b604051629c6fbb60e21b81526001600160a01b039091166004820152fd5b34610146576000366003190112610146576102b4611bad565b506102bd611bad565b6102ef6040516102cc81611368565b6002815260403660208301376020830181905263ea15602d60e01b91829161141c565b526102fd6020830151611429565b5260405160a081018181106001600160401b038211176107635760405260048152608036602083013780604083015261033d630c76670b60e21b9161141c565b5263717493c760e01b6103536040830151611429565b5263fd5e71c560e01b6103696040830151611439565b5260408101518051600310156107b957608063ca9b97f360e01b9101526040519061039382611368565b6001825260006020830152600060408301526040516103b181611368565b600281526000602082015260006040820152604051926103d084611368565b6002845260005b60408110610779575061041f90839460e0610451950152604051906103fb8261134d565b630c76670b60e21b8252602082015260e0850151906104198261141c565b5261141c565b506040519061042d8261134d565b63ca9b97f360e01b8252602082015260e08301519061044b82611429565b52611429565b506040519061045f82611368565b60038252600060208301526000604083015260405161047d81611368565b60028152600060208201526001604082015260405192608084018481106001600160401b03821117610763576040526003845260005b60608110610723575061052890839461010061055b9501526104fb6040516104da8161134d565b63717493c760e01b815260208101839052610100870151906104198261141c565b50604051906105098261134d565b63fd5e71c560e01b825260208201526101008501519061044b82611429565b50604051906105368261134d565b63ca9b97f360e01b825260208201526101008301519061055582611439565b52611439565b5060808101906001825260a081019060018252604051928392602084526105db6105c56105af61059986516101a060208a01526101c0890190611211565b6020870151888203601f190160408a0152611211565b6040860151878203601f19016060890152611211565b6060850151868203601f19016080880152611211565b9151151560a085015251151560c084015260c082015190601f198482030160e0850152815180825260208201916020808360051b8301019401926000915b8383106106cc5787806106c8896106418a60e0830151601f1986830301610100870152611294565b6106b361069a6106816106686101008601519461012095601f198a830301878b0152611294565b938501519361014094601f1989830301868a0152611294565b928401519261016093601f198883030185890152611294565b918301519161018092601f1987830301848801526112e8565b910151838203601f19016101a08501526112e8565b0390f35b91939596509193602080610710600193601f19868203018752606060408b51878060a01b038151168452858101511515868501520151918160408201520190611211565b9701930193019092879695949293610619565b6020906040516107328161134d565b6000815260405161074281611368565b600081526000848201526000604082015283820152828288010152016104b3565b634e487b7160e01b600052604160045260246000fd5b6020906040516107888161134d565b6000815260405161079881611368565b600081526000848201526000604082015283820152828288010152016103d7565b634e487b7160e01b600052603260045260246000fd5b610a6d565b346101465760206107ed6107e7366111cd565b91611902565b604051908152f35b34610146576020366003190112610146576004356001600160401b0381116101465761082590369060040161103f565b50506108303361169f565b60608101906001805b15610858575b61fffe1916825260808120805460009182905590610839565b600181161580610875575b61083f57600183526080822060009055005b50801515610863565b346101465761088c366111cd565b505060405163d623472560e01b815260049150fd5b61117d565b34610146576000366003190112610146576106c86108cb6108c63361145d565b6116c6565b60405191829182611139565b3461014657602080600319360112610146576001600160401b03906004358281116101465761090a90369060040161103f565b929091333b15610a5b5761091d3361169f565b91600194608060609487868201522054868115918215610a51575b505015610a4057840193828186031261014657803591821161014657019280601f8501121561014657833561096c816113a4565b9461097a6040519687611383565b818652838087019260051b820101928311610146578301905b828210610a29575050505081519160005b8381106109ad57005b6109b78183611449565b516001600160a01b03908181168015610a1157506109e190851b6001600160601b03191633611719565b156109ee575084016109a4565b6109fa60249284611449565b5160405163512c6ac960e11b815291166004820152fd5b6024906040519063d3d0f65960e01b82526004820152fd5b838091610a358461102b565b815201910190610993565b60405162dc149f60e41b8152600490fd5b1690508688610938565b604051635d3944c960e11b8152600490fd5b3461014657610a7b3661106c565b5050505050600460405163d623472560e01b8152fd5b34610146576020366003190112610146576106c86108cb6108c6610ab3610fff565b61145d565b34610146576040366003190112610146576001600160401b0360043581811161014657610ae9903690600401611109565b610af1611015565b50610afb816113a4565b91610b096040519384611383565b818352610b15826113a4565b60209490601f199081018660005b828110610cce5750505060005b848110610b9557604080518881528751818a01819052600092600582901b83018101918a8c01918c9085015b828710610b695785850386f35b909192938280610b85600193603f198a820301865288516110e4565b9601920196019592919092610b5c565b8060051b840135605e198536030181121561014657840190610bb6826113bb565b916000610bc660408301836113cf565b6040516338997b1160e01b81526001600160a01b039096166004870152928b0135602486015260606044860152606485018390529193918290608490869082840137828187840101528187601f80980116810103018183335af1908115610cc257600091610c50575b5060019250610c3e8289611449565b52610c498188611449565b5001610b30565b90503d806000833e610c628183611383565b810190898183031261014657805186811161014657829101938401121561014657825192610c8f84611401565b91610c9d6040519384611383565b8483528a858301011161014657600193610cbc918b80850191016110c1565b89610c2f565b6040513d6000823e3d90fd5b60608882018301528101610b23565b346101465760403660031901126101465760206080610cfa610fff565b610121610d05611015565b9161169f565b3461014657600036600319011261014657610d24611c20565b50610d2d611c20565b60405190610d3a8261134d565b601282526020917129b2b9b9b4b7b71025b2bc9028363ab3b4b760711b838201528152604051610d698161134d565b60058152640312e302e360dc1b8382015282820190815260405190610d8d8261134d565b6007825266416c6368656d7960c81b848301526040830191825260405191610db48361134d565b60138352724d6f646966792053657373696f6e204b65797360681b8584015260405190610de08261134d565b60019384835260005b878110610ee9575090610e27610e6d926060880194855260405190610e0d8261134d565b63ca9b97f360e01b8252898201528451906104198261141c565b50610e5c610e46604051978989525160808a8a015260a08901906110e4565b945194601f1995868983030160408a01526110e4565b9051848783030160608801526110e4565b9051948285830301608086015285519182815281810182808560051b8401019801946000925b858410610ea057888a0389f35b909192939495968580610ed68c8686869f030188526040838d5163ffffffff60e01b8151168452015191818582015201906110e4565b9b99019796959190910193019190610e93565b968093949597604051610efb8161134d565b60008152606083820152828288010152019694939296610de9565b346101465760403660031901126101465760043560ff811603610146576024356001600160401b03811161014657610f5290369060040161103f565b505060405163d623472560e01b8152600490fd5b346101465760403660031901126101465760206107ed610f84610fff565b610f8c611015565b90611843565b34610146576020366003190112610146576004359063ffffffff60e01b8216809203610146576020916368caf97960e01b8114908115610fd4575b5015158152f35b63ea15602d60e01b811491508115610fee575b5083610fcd565b6301ffc9a760e01b14905083610fe7565b600435906001600160a01b038216820361014657565b602435906001600160a01b038216820361014657565b35906001600160a01b038216820361014657565b9181601f84011215610146578235916001600160401b038311610146576020838186019501011161014657565b9060806003198301126101465760043560ff8116810361014657916024356001600160a01b0381168103610146579160443591606435906001600160401b038211610146576110bd9160040161103f565b9091565b60005b8381106110d45750506000910152565b81810151838201526020016110c4565b906020916110fd815180928185528580860191016110c1565b601f01601f1916010190565b9181601f84011215610146578235916001600160401b038311610146576020808501948460051b01011161014657565b6020908160408183019282815285518094520193019160005b828110611160575050505090565b83516001600160a01b031685529381019392810192600101611152565b346101465760a0366003190112610146576004356001600160a01b03811603610146576060366023190112610146576084356001600160401b038111610146576111cb90369060040161103f565b005b906003196060818401126101465760043560ff811681036101465792602435916001600160401b038311610146578261016092030112610146576004019060443590565b90815180825260208080930193019160005b828110611231575050505090565b83516001600160e01b03191685529381019392810192600101611223565b6005111561125957565b634e487b7160e01b600052602160045260246000fd5b60408091805161127e8161124f565b845260ff60208201511660208501520151910152565b90815180825260208080930193019160005b8281106112b4575050505090565b909192938260806001926112dc83895163ffffffff60e01b815116845201518483019061126f565b019501939291016112a6565b90815180825260208080930193019160005b828110611308575050505090565b909192938260e06001926113416040895163ffffffff851b8151168452611335868201518786019061126f565b0151608083019061126f565b019501939291016112fa565b604081019081106001600160401b0382111761076357604052565b606081019081106001600160401b0382111761076357604052565b90601f801991011681019081106001600160401b0382111761076357604052565b6001600160401b0381116107635760051b60200190565b356001600160a01b03811681036101465790565b903590601e198136030182121561014657018035906001600160401b0382116101465760200191813603831361014657565b6001600160401b03811161076357601f01601f191660200190565b8051156107b95760200190565b8051600110156107b95760400190565b8051600210156107b95760600190565b80518210156107b95760209160051b010190565b60018060a01b031690604051828152639cc6c92360e01b928360208301526000918260408201526060810191600193838580955260809385858220549284975b611532575b505050506114af846113a4565b966114bd6040519889611383565b848852601f196114cc866113a4565b013660208a0137878515611528575060405192835260208301528060408301528460608301905b8583106115035750505050505050565b8152858484205461ffff19811661151a858c611449565b5292019161fffe19166114f3565b9750505050505050565b90919296828816158061157a575b156115715782019661fffe198116906002161561156957815285832054915b908193929361149d565b50839161155f565b968291506114a2565b50871515611540565b60018060a01b031690604051828152639cc6c92360e01b928360208301526000918260408201526060810191600193838580955260809385858220549284975b61164e575b505050506115d5846113a4565b966115e36040519889611383565b848852601f196115f2866113a4565b013660208a0137878515611528575060405192835260208301528060408301528460608301905b8583106116295750505050505050565b8152858484205461ffff198116611640858c611449565b5292019161fffe1916611619565b909192968288161580611696575b1561168d5782019661fffe198116906002161561168557815285832054915b90819392936115c3565b50839161167b565b968291506115c8565b5087151561165c565b604080516001600160a01b039092168252639cc6c92360e01b602083015260009082015290565b805160005b8181106116d757505090565b806116e460019285611449565b5160601c6116f28286611449565b52016116cb565b91908110156107b95760061b0190565b91908110156107b95760051b0190565b9061ffff19169081156117a157604080516001600160a01b039092168252639cc6c92360e01b6020830152600090820152606081018281526080822080549092906117985760016080925220918254928315801561178e575b1561178257600193505555600190565b90600217905555600190565b5060018416611772565b50505050600090565b5050600090565b604080516001600160a01b039092168252639cc6c92360e01b602083015260009082015261fffe199261ffff199092169183168015801561183b575b61179857606082019081526080822091825493808686160361183057608092522091825480156118265760009461fffc60028316931691161717905555600190565b5050505050600090565b505050505050600090565b5082156117e4565b6108c661184f91611583565b805190600193846000905b84821061188257604051633c3629c360e11b81526001600160a01b0387166004820152602490fd5b6001600160a01b03806118958487611449565b5116908716146118c15750856001600160601b03196118b48386611449565b5160601b1691019061185a565b9450509350505090565b9291926118d782611401565b916118e56040519384611383565b829481845281830111610146578281602093846000960137010152565b60ff161561191c5760405163d623472560e01b8152600490fd5b60609061192b828201826113cf565b8060041161014657810190604091826003198383030112610146576001600160401b03600483013581811161014657830190826023830112156101465760048201359060249561197a836113a4565b9461198782519687611383565b8386528760208097019460051b8601019481861161014657888101945b868610611a7a57505050505050505001359160018060a01b039384841693848103610146576080917f19457468657265756d205369676e6564204d6573736167653a0a333200000000600052601c52603c60002092611a023361169f565b91811b6001600160601b031916908201522054611a23575b50505050600190565b611a3f611a3883610140611a459501906113cf565b36916118cb565b90611af4565b611a518194929461124f565b159283611a6e575b505050611a695738808080611a1a565b600090565b16149050388080611a59565b85358581116101465782018c602319828603011261014657845191611a9e83611368565b611aa98c830161102b565b8352604491828101358b8501526064810135908882116101465701928560438501121561014657611ae586858f958e97968897013591016118cb565b878201528152019501946119a4565b906041815114600014611b1e576110bd916020820151906060604084015193015160001a90611b28565b5050600090600290565b9291906fa2a8918ca85bafe22016d0b997e4df60600160ff1b038311611ba15791608094939160ff602094604051948552168484015260408301526060820152600093849182805260015afa15611b945781516001600160a01b03811615611b8e579190565b50600190565b50604051903d90823e3d90fd5b50505050600090600390565b604051906101a082018281106001600160401b038211176107635760405281610180606091828152826020820152826040820152828082015260006080820152600060a08201528260c08201528260e0820152826101008201528261012082015282610140820152826101608201520152565b60405190608082018281106001600160401b038211176107635760405260608083818152816020820152816040820152015256fea2646970667358221220e46e533a4c6295ca04a176e3e05fbb8cc4587224335aa50e4d8325e80aff428364736f6c63430008150033", + "nonce": "0x4e", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x8175ec950981e3c7254ab70faa68a8cb085dba2c107ed546a634e462ad48769f", + "transactionType": "CREATE", + "contractName": "SessionKeyPermissionsPlugin", + "contractAddress": "0x78462720341714Dcab90C6d5B9a47fAAA6fdADC6", + "function": null, + "arguments": null, + "transaction": { + "type": "0x02", + "from": "0x7f89ed1f3f0d52d303904101305471bca3cde710", + "gas": "0x2c33a6", + "value": "0x0", + "data": "0x6080806040523461001657612752908161001c8239f35b600080fdfe60808060405260048036101561001457600080fd5b60003560e01c91826301ffc9a71461150f575081630c7ecd84146107b35781631128186d146114c4578163118a5389146112a45781632d106419146111f457816346d60eb214610ff6578163676c3dc214610f0e578163684d07c514610fa35781636d61fe7014610ee75781637060f7f714610f135781637f42433814610f0e57816388e18ce414610eec5781638a91b0e314610ee757816398e2c27a14610eab578163991ef7f714610dea578163a1b9d5f414610da8578163af87348314610d87578163b7967b7114610d3d578163bb319893146107f357508063bc3b9246146107b8578063bfd151c1146107b3578063c776313014610272578063cfbb14ad14610217578063e5282cbd146101a65763e8a932df1461013457600080fd5b346101a15760803660031901126101a15761014d61157b565b610155611591565b61015d6115a7565b916064356001600160e01b0319811681036101a157602093838361019061018960ff97610195976118e9565b9283611932565b611ab9565b54166040519015158152f35b600080fd5b346101a15760403660031901126101a1576101dd6101c261157b565b6101ca611591565b906101d861018983836118e9565b6119ff565b805460681c60ff161561020d57600301546020906001600160a01b03165b6040516001600160a01b039091168152f35b50602060006101fb565b346101a15760603660031901126101a157604061025861023561157b565b61023d611591565b6102456115a7565b9161025361018983836118e9565b611a6d565b5460ff8251918181161515835260081c1615156020820152f35b346101a15760003660031901126101a15761028b611f5b565b50610294611f5b565b6102c66040516102a3816119a8565b6002815260403660208301376020830181905263ea15602d60e01b918291611fce565b526102d46020830151611ff1565b5261049361030a6040516102e7816119c3565b6003815260603660208301376040840181905263bb31989360e01b918291611fce565b5263991ef7f760e01b90816103226040860151611ff1565b5261046e637060f7f760e01b928361033d6040880151612001565b526103f260405161034d816119a8565b600281526000602082015260006040820152610367612044565b60e089015261039960405161037b8161195c565b86815282602082015260e08a01519061039382611fce565b52611fce565b506103c76040516103a98161195c565b84815282602082015260e08a0151906103c182611ff1565b52611ff1565b50604051906103d58261195c565b868252602082015260e0880151906103ec82612001565b52612001565b5061044860405193610403856119a8565b60028552600060208601526001604086015261041d612044565b610100890152604051906104308261195c565b81528460208201526101008801519061039382611fce565b50604051906104568261195c565b8152826020820152610100860151906103c182611ff1565b506040519161047c8361195c565b82526020820152610100830151906103ec82612001565b506040516104a08161195c565b6001815260005b6020811061078857506101208201526105036040516104c5816119a8565b600181526000602082015260006040820152604051906104e48261195c565b630c76670b60e21b825260208201526101208301519061039382611fce565b506040516105108161195c565b6001815260005b602081106107505750610160820152610596604051610535816119a8565b600181526001602082015260006040820152604051610553816119a8565b60008152600060208201526000604082015260405191610572836119a8565b630c76670b60e21b8352602083015260408201526101608301519061039382611fce565b506040518091602082526106036105ed6105d76105c184516101a060208801526101c08701906117b6565b6020850151868203601f190160408801526117b6565b6040840151858203601f190160608701526117b6565b6060830151848203601f190160808601526117b6565b6080820151151560a084015260a0820151151560c084015260c082015190601f198482030160e0850152815180825260208201916020808360051b8301019401926000915b8383106106f95787806106f5896106c261068e6106768c60e0850151601f1988830301610100890152611830565b610100840151868203601f1901610120880152611830565b6106ac6101208401519161014092601f198883030184890152611830565b90830151858203601f1901610160870152611830565b6106e06101608301519161018092601f198783030184880152611884565b910151838203601f19016101a0850152611884565b0390f35b9193959650919360208061073d600193601f19868203018752606060408b51878060a01b0381511684528581015115158685015201519181604082015201906117b6565b9701930193019092879695949293610648565b60209060405161075f816119a8565b6000815261076b612025565b83820152610777612025565b604082015282828501015201610517565b6020906040516107978161195c565b600081526107a3612025565b83820152828285010152016104a7565b611653565b346101a15760403660031901126101a15760a06107e46107d661157b565b6107de611591565b90611b34565b6107f160405180926116b7565bf35b346101a1576003196040368201126101a15761080d61157b565b6024928335906001600160401b03908183116101a157366023840112156101a15782810135958287116101a15780840195600588811b93368486890101116101a15761085e889a9697949a336118e9565b6108688982611932565b61087281336119ff565b9360005b888110610951575050505050506040519660209284848a01858b52526040808a01918a0101979560009360421981360301905b8786106108e3576001600160a01b038a16337f5e009edbae60e9d9dad056b2eba09cf2520596f0a9dbbabf9716ef5802e783768e8e038fa3005b909192939495969799603f198c82030185528a35838112156101a1578201906044878301359201918581116101a15780360383136101a15789828280600196849695859652848401376000828201840152601f01601f191601019c01999897919091019594019291906108a9565b6109668d829a9e989b9a871b8c01018d611c94565b9190858310610d2c576001600160e01b0319813581169390630e1adbe560e31b85016109ce5750809192935086116101a157838260209281010301126101a1578401359060038210156101a15760019160ff801989541691161787555b019b9798959b610876565b9193639e7345eb60e01b8103610a5e575080915086116101a157838360609281010301126101a1578188610a44610a31610a226044610a1b610a138c60019a016115bd565b95870161267a565b950161267a565b92868060a01b03168833611a6d565b92839060ff801983541691151516179055565b815461ff00191690151560081b61ff00161790555b6109c3565b630646f00160e11b8103610acf57508087116101a157848460609281010301126101a157610a8d8684016115bd565b8984013591821682036101a157610abe610a5992610aaf60446001970161267a565b92868060a01b03168833611ab9565b9060ff801983541691151516179055565b909150639a37b11360e01b8103610b4857508086116101a157838360409281010301126101a15781610b0f89610b08886001960161264d565b920161264d565b66ffffffffffff0089549165ffffffffffff60381b9060381b169260081b16906cffffffffffffffffffffffff001916171787556109c3565b909290632ce89bd760e21b8103610ba157508286116101a157610b758187600195610b8594019101612660565b9060068a019160028b0191612687565b875460ff60801b1916901560801b60ff60801b161787556109c3565b637b1f089360e01b8103610c3f575090918086116101a157838260609281010301126101a157610bd28582016115bd565b610bde6044830161264d565b906001600160a01b03168015610c2e576001939291610c01610c13928833611a6d565b9260028401918c878601920135612687565b815462ff0000191690151560101b62ff0000161790556109c3565b60405163c1ab6dc160e01b81528790fd5b63585ca4a560e01b8103610cb257508286116101a157610c698187600195610c8694019101612660565b90895460ff8160781c16610ca3575b50878a0191858b0191612687565b875460ff60701b191690151560701b60ff60701b161787556109c3565b60ff60781b19168a5538610c78565b90929063b85631d760e01b03610d2c578086116101a157838360209281010301126101a1576001916001600160a01b0390610cee9087016115bd565b1680610d055750865460ff60681b191687556109c3565b8260681b60ff60681b198954161788556003880190838060a01b03198254161790556109c3565b604051633e57645160e21b81528690fd5b346101a15760403660031901126101a15760ff610d5b6101c261157b565b5416604051906003811015610d7257602092508152f35b602183634e487b7160e01b6000525260246000fd5b346101a157610d9536611772565b505060405163d623472560e01b81529050fd5b346101a15760403660031901126101a15760c0610dd4610dc661157b565b610dce611591565b90611bd2565b610de160405180936116b7565b151560a0820152f35b346101a15760403660031901126101a157610e0361157b565b90610e0e82336118e9565b610e8a573360005260006020526040600020908154906000198214610e755750906001610e4092018091558233611c70565b602435906001600160a01b0316337f886c546ebd7ab8d6a406e5284ea019784616cf727fda33cbd4f7c82bfbb3c7cf600080a4005b601190634e487b7160e01b6000525260246000fd5b604051634f2034ad60e11b81526001600160a01b0390921690820152602490fd5b346101a15760403660031901126101a1576040610ec96101c261157b565b5465ffffffffffff825191818160081c16835260381c166020820152f35b611742565b346101a1576020610f06610eff36611772565b5090611cc6565b604051908152f35b6116f2565b346101a15760403660031901126101a157610f2c61157b565b610f34611591565b90610f3f81336118e9565b91610f4a8284611932565b610f79610f563361190e565b93600060018060a01b038095169586606082015260208151910120558233611c70565b1690337f67a108f883dad01f07fc4e8705cea092f645240361df1a3b23b550258bb80620600080a4005b346101a15760403660031901126101a157610fbf6101c261157b565b80549060ff8260781c16610fcf57005b60ff60781b199091168155600101805465ffffffffffff19164265ffffffffffff16179055005b346101a157611004366115fe565b60019460ff945090925083168414611069575b50505050604051602081018181106001600160401b03821117611054576106f5925060405260008152604051918291602083526020830190611677565b604183634e487b7160e01b6000525260246000fd5b8185116101a1578461107e92820191016120c7565b81519192916001600160a01b03918216916000919061109d84336118e9565b6110a78582611932565b6110b181336119ff565b968860005b8581106111595750505050505082845460801c161561110e575b50508154908160781c166110e5575b80611017565b60ff60781b1916815501805465ffffffffffff19164265ffffffffffff161790558180806110df565b61112190600685019060028601906124be565b1561112c57806110d0565b6040805163c8cfd93b60e01b8152338188019081526001600160a01b039093166020840152918291010390fd5b6111736111668285612011565b5197602089015190612201565b96611182868251168633611a6d565b8a815460101c16611198575b50500189906110b6565b819293506111b2876111bf93511660408501519033612554565b8d600283019201906124be565b156111cd5790818b9261118e565b5160405163160204d160e21b815233818e0152602481018990529085166044820152606490fd5b346101a15760603660031901126101a15760a061124961121261157b565b61121a611591565b6112226115a7565b9161122b611b09565b5061123961018983836118e9565b61124382826119ff565b50611a6d565b60ff815460101c1690600281015490600160038201549101549065ffffffffffff92604051946112788661198d565b1515855260208501526040840152818160301c1660608401521660808201526107f160405180926116b7565b346101a15760003660031901126101a1576112bd612093565b506112c6612093565b604051906112d38261195c565b601e82526020917f53657373696f6e204b6579205065726d697373696f6e7320506c7567696e000083820152815260405161130d8161195c565b60058152640312e302e360dc1b83820152828201908152604051906113318261195c565b6007825266416c6368656d7960c81b8483015260408301918252604051916113588361195c565b601e83527f4d6f646966792053657373696f6e204b6579205065726d697373696f6e730000858401526040519061138e8261195c565b60019384835260005b8781106114975750906113d561141b9260608801948552604051906113bb8261195c565b63bb31989360e01b82528982015284519061039382611fce565b5061140a6113f4604051978989525160808a8a015260a0890190611677565b945194601f1995868983030160408a0152611677565b905184878303016060880152611677565b9051948285830301608086015285519182815281810182808560051b8401019801946000925b85841061144e57888a0389f35b9091929394959685806114848c8686869f030188526040838d5163ffffffff60e01b815116845201519181858201520190611677565b9b99019796959190910193019190611441565b9680939495976040516114a98161195c565b60008152606083820152828288010152019694939296611397565b346101a15760403660031901126101a157803560ff8116036101a1576024356001600160401b0381116101a1576114fe90369083016115d1565b505060405163d623472560e01b8152fd5b90346101a15760203660031901126101a157359063ffffffff60e01b82168092036101a15760209163e7de0f3960e01b8114908115611550575b5015158152f35b63ea15602d60e01b81149150811561156a575b5083611549565b6301ffc9a760e01b14905083611563565b600435906001600160a01b03821682036101a157565b602435906001600160a01b03821682036101a157565b604435906001600160a01b03821682036101a157565b35906001600160a01b03821682036101a157565b9181601f840112156101a1578235916001600160401b0383116101a157602083818601950101116101a157565b9060806003198301126101a15760043560ff811681036101a157916024356001600160a01b03811681036101a1579160443591606435906001600160401b0382116101a15761164f916004016115d1565b9091565b346101a157611661366115fe565b5050505050600460405163d623472560e01b8152fd5b919082519283825260005b8481106116a3575050826000602080949584010152601f8019910116010190565b602081830181015184830182015201611682565b60809080511515835260208101516020840152604081015160408401528160608201519165ffffffffffff8093166060860152015116910152565b346101a15760a03660031901126101a1576004356001600160a01b038116036101a15760603660231901126101a1576084356001600160401b0381116101a1576117409036906004016115d1565b005b346101a15760203660031901126101a1576004356001600160401b0381116101a1576117409036906004016115d1565b906003196060818401126101a15760043560ff811681036101a15792602435916001600160401b0383116101a15782610160920301126101a1576004019060443590565b90815180825260208080930193019160005b8281106117d6575050505090565b83516001600160e01b031916855293810193928101926001016117c8565b8051600581101561181a576040918291845260ff60208201511660208501520151910152565b634e487b7160e01b600052602160045260246000fd5b90815180825260208080930193019160005b828110611850575050505090565b9091929382608060019261187883895163ffffffff60e01b81511684520151848301906117f4565b01950193929101611842565b90815180825260208080930193019160005b8281106118a4575050505090565b909192938260e06001926118dd6040895163ffffffff851b81511684526118d186820151878601906117f4565b015160808301906117f4565b01950193929101611896565b6118f29061190e565b6001600160a01b03909116606082015280516020909101205490565b90604051916080830160405260608352602083015263068076b960e21b6040830152565b1561193a5750565b60405163099326a960e01b81526001600160a01b039091166004820152602490fd5b604081019081106001600160401b0382111761197757604052565b634e487b7160e01b600052604160045260246000fd5b60a081019081106001600160401b0382111761197757604052565b606081019081106001600160401b0382111761197757604052565b608081019081106001600160401b0382111761197757604052565b90601f801991011681019081106001600160401b0382111761197757604052565b9060405190602082019260808301604052606083528352630b5ff94b60e11b908160408401526040519060208201928352602482015260248152611a42816119a8565b5190519060208110611a5b575b50606082015251902090565b6000199060200360031b1b1638611a4f565b90611ab6926040519260a0840160405260808452602084015263634c29f560e01b60408401526001600160601b03199060601b1690606083015260808201526020815191012090565b90565b6040805160a081018252608080825260208201938452630d50536f60e41b9282019290925260608101939093526001600160e01b03199094166001600160a01b0390931692909217908301522090565b60405190611b168261198d565b60006080838281528260208201528260408201528260608201520152565b90611b4f91611b41611b09565b506101d861018983836118e9565b805460801c60ff16611ba2576006810154906002600782015491015465ffffffffffff9160405193611b808561198d565b6001855260208501526040840152818160301c16606084015216608082015290565b50604051611baf8161198d565b600081526000602082015260006040820152600060608201526000608082015290565b90611bdf91611b41611b09565b9081549160ff808460781c169360701c16600014611c3f576004810154906001600582015491015465ffffffffffff9160405193611c1c8561198d565b6001855260208501526040840152818160301c1660608401521660808201529190565b50604051611c4c8161198d565b60008152600060208201526000604082015260006060820152600060808201529190565b611c799061190e565b6001600160a01b039091166060820152805160209091012055565b903590601e19813603018212156101a157018035906001600160401b0382116101a1576020019181360383136101a157565b60ff80911615611ce25760405163d623472560e01b8152600490fd5b611cef6060830183611c94565b90816004116101a1576004611d0792820191016120c7565b9091906001600160a01b03908116611d1f81336118e9565b91611d2a8284611932565b611d3483336119ff565b91825465ffffffffffff8160081c169660009080518015159783925b828410611f155750505050868260801c1615611edd575b5060701c8516611e2d575b50815495848760681c16611dc0575b50505060009015600014611dba57506001915b65ffffffffffff60d01b9060d01b169265ffffffffffff60a01b9060681b169116171790565b91611d94565b80610120611dcf920190611c94565b6bffffffffffffffffffffffff19913582811694929160148110611e18575b50505083611e02575b505050388080611d81565b6003919293500154169060601c14388080611df7565b60140360031b82901b16169250388080611dee565b9492611e9c90611e41610120890189611c94565b159050611ec257611e8d84611e88611e7d611e728c6080611e6a8d60035b1660a0840135612224565b910135612201565b60c08d013590612201565b60e08c013590612224565b6123c4565b919081611eba575b50946126ff565b94602087013560401c03611eb1575b38611d72565b60009250611eab565b905038611e95565b611e8d84611e88611e7d611e728c6080611e6a8d6001611e5f565b86919795611ef7611f06926006880190600289019061233d565b919081611f0d575b50966126ff565b9690611d67565b905038611eff565b909192986001908b611f2a6111668d86612011565b9682611f3d575b50509901929190611d50565b611f5492508560408c835116920151928a16612268565b8b38611f31565b604051906101a082018281106001600160401b038211176119775760405281610180606091828152826020820152826040820152828082015260006080820152600060a08201528260c08201528260e0820152826101008201528261012082015282610140820152826101608201520152565b805115611fdb5760200190565b634e487b7160e01b600052603260045260246000fd5b805160011015611fdb5760400190565b805160021015611fdb5760600190565b8051821015611fdb5760209160051b010190565b60405190612032826119a8565b60006040838281528260208201520152565b604090815191612053836119c3565b60038352600083815b6060811061206a5750505050565b60209084516120788161195c565b84815282612084612025565b8183015282850101520161205c565b60405190608082018281106001600160401b0382111761197757604052606080838181528160208201528160408201520152565b919060409081848203126101a1576001600160401b0384358181116101a157850190601f938385840112156101a157823593602095838611611977578560051b92805196612117898601896119de565b87528780880194870101958387116101a157888101945b87861061214957505050505050505090611ab69193016115bd565b85358781116101a157601f19916060918401808803840183136101a15785519261217d8e612176866119a8565b83016115bd565b8452818701358e850152810135908a82116101a157019187603f840112156101a1578c830135938a85116121ec576121bd8e8851928a88011601826119de565b848152888786860101116101a15760008e8681978a83980183860137830101528682015281520195019461212e565b60246000634e487b7160e01b81526041600452fd5b9190820180921161220e57565b634e487b7160e01b600052601160045260246000fd5b8181029291811591840414171561220e57565b906020825192015163ffffffff60e01b90818116936004811061225957505050565b60040360031b82901b16169150565b6001949361227590612237565b90612281848433611a6d565b90600381101561181a57806122cf57509091929394505460ff8116156122c65760081c60ff16156122be5760ff926122b99233611ab9565b541690565b505050600190565b50505050600090565b86146122dc575b50505050565b949091929394549060ff82161561231b575060081c60ff16156123135760ff926123069233611ab9565b54161590388080806122d6565b505050600090565b935050505090565b91909165ffffffffffff8080941691160191821161220e57565b9291909260008094549065ffffffffffff808360301c169160018601549554958501948086106123b8578361237757505050505011159190565b909294958092949611156000146123945750505050505060019190565b94969394929392116123b057505090611ab69160019416612323565b945092915050565b50965050505050508190565b9190916000809360018101549165ffffffffffff8084169360301c1692600583019384549160048501549480549060ff8260781c16948901958987106124af5784612424575050505050509061241e911115928254612201565b90559190565b90929680879c93969a95999c111560001461245c575050505050506001955560009260001461245757611ab69250612323565b505090565b909295508397949a96989193111590816124a6575b501561249b57505061248590600195612323565b60ff60781b19909616600160781b179055559190565b975095945050505050565b90501538612471565b9a505050505050505050508190565b9181549165ffffffffffff90818460301c16916001845494019485549382811592831561253c575b50505060001461251b5750508301928310908115612511575b5061250a5755600190565b5050600090565b90508211386124ff565b939150939182116122c65755421665ffffffffffff19825416179055600190565b612547935016612323565b81429116113882816124e6565b91906001600160e01b031961256883612237565b1663a9059cbb60e01b81036125915750509050604481511061258b576044015190565b50600090565b63095ea7b360e01b146125a657505050600090565b604482511061231357602482810151604051636eb1769f60e11b81526001600160a01b039586166004820152908516918101919091529260209184916044918391165afa9182156126415760009261260a575b50604401518181111561250a570390565b90916020823d8211612639575b81612624602093836119de565b810103126126365750519060446125f9565b80fd5b3d9150612617565b6040513d6000823e3d90fd5b359065ffffffffffff821682036101a157565b91908260409103126101a157611ab660208335930161264d565b359081151582036101a157565b92939260001981036126a0575050506000600181930155565b6001945565ffffffffffff908254918065ffffffffffff60301b8360301b16928365ffffffffffff60301b19861617865516156000146126ec5750505065ffffffffffff198154169055565b4216916001600160601b03191617179055565b9065ffffffffffff8082169083161115612717575090565b90509056fea264697066735822122065062352a5bc69f9af0e807ac2419d3184608490a0d9ceabe04c73defebd3f4964736f6c63430008150033", + "nonce": "0x4f", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [], + "libraries": [], + "pending": [ + "0x09d250a7c7e7c8414232c10a3487fc301d10bd70b32b8f66c01db654f5471fbe", + "0x80c2f62037e084f594ce6d0be7de0119d62e5d9faefe5f903515d1b76d9fea40", + "0x0f13a3b7e8528a2fb6f1b4ceb43330feab35d56a3ffc7ffc0cbe8bd16ea4d7ba", + "0xcefba29b52d19365a4b859228052e7415eed0576786ea4330dbe977cf2dbf7cc", + "0xb2179964a1836d4510adb53395bbf338881895fb1943e077bfffdea583a5fa57", + "0x5cb7347b960e28bd3bf3a4958f438cd477ed9a2bdf2c2862ade7b33ce951b14c", + "0x045e474abf86398ebbf1814e8efd5407a1800a52d6d3bc30be6e0e7d2536db54", + "0x8d523325b3f21dedb6e264a1da4295ef0e00cc59c3d2c26d6dcea079f6b00e47", + "0x8175ec950981e3c7254ab70faa68a8cb085dba2c107ed546a634e462ad48769f" + ], + "returns": {}, + "timestamp": 1700676223, + "chain": 11155111, + "multi": false, + "commit": "0e3fd1e" +} \ No newline at end of file diff --git a/broadcast/Deploy.s.sol/11155111/run-1700676229.json b/broadcast/Deploy.s.sol/11155111/run-1700676229.json new file mode 100644 index 00000000..5bacbb1a --- /dev/null +++ b/broadcast/Deploy.s.sol/11155111/run-1700676229.json @@ -0,0 +1,444 @@ +{ + "transactions": [ + { + "hash": "0x09d250a7c7e7c8414232c10a3487fc301d10bd70b32b8f66c01db654f5471fbe", + "transactionType": "CREATE", + "contractName": "UpgradeableModularAccount", + "contractAddress": "0xb2b748c2557c552B8636862E41aB3649319dD045", + "function": null, + "arguments": [ + "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" + ], + "transaction": { + "type": "0x02", + "from": "0x7f89ed1f3f0d52d303904101305471bca3cde710", + "gas": "0x6888c4", + "value": "0x0", + "data": "0x60c0346200010557601f62005f0038819003918201601f19168301916001600160401b038311848410176200010a578084926020946040528339810103126200010557516001600160a01b038116810362000105573060805260a0527fade46bbfcf6f898a43d541e42556d456ca0bf9b326df8debc0f29d3f811a0300805460ff8160081c16620000f35760ff80821603620000e2575b604051615ddf90816200012182396080518181816108a801526109ac015260a05181818161076801528181611065015281816111ac015281816119e50152818161375301526148ab0152f35b60ff191660ff179055388062000096565b60405163593ae07560e01b8152600490fd5b600080fd5b634e487b7160e01b600052604160045260246000fdfe60806040526004361015610026575b36156100245761001c613720565b602081519101f35b005b60003560e01c806301ffc9a7146101465780630b0faea11461014157806334fcd5be1461013c57806338997b11146101375780633a0cac56146101325780633a871cdd1461012d5780634f1ef2861461012857806352d1902d14610123578063642f9dd41461011e57806377da2b74146101195780638d1121841461011457806394ed11e71461010f578063ad60fa261461010a578063b0d691fe14610105578063b61d27f614610100578063ceaf1309146100fb578063d087d288146100f65763e69e24a80361000e57611353565b611178565b611118565b611094565b61104f565b610e64565b610cb1565b610bd3565b610a27565b6109fa565b610998565b61086c565b61072e565b61069a565b61048e565b61033b565b610209565b610162565b6001600160e01b031981160361015d57565b600080fd5b3461015d57602036600319011261015d5760206101896004356101848161014b565b61485d565b6040519015158152f35b6001600160a01b0381160361015d57565b35906101af82610193565b565b60208082019080835283518092528060408094019401926000905b8382106101db57505050505090565b845180516001600160581b0319908116885290840151168684015294850194938201936001909101906101cc565b3461015d57604036600319011261015d5761025d610251600161024b61024660043561023481610193565b602435906102418261014b565b61569c565b61147b565b0161155d565b604051918291826101b1565b0390f35b9181601f8401121561015d578235916001600160401b03831161015d576020808501948460051b01011161015d57565b60005b8381106102a45750506000910152565b8181015183820152602001610294565b906020916102cd81518092818552858086019101610291565b601f01601f1916010190565b602080820190808352835180925260408301928160408460051b8301019501936000915b84831061030d5750505050505090565b909192939495848061032b600193603f198682030187528a516102b4565b98019301930191949392906102fd565b60208060031936011261015d576004356001600160401b03811161015d57610367903690600401610261565b91610370614899565b93909161037c826136d6565b9360005b8381106103a15761025d866103958988614903565b604051918291826102d9565b806103f26103b26001938786613a0e565b356103bc81610193565b856103c8848988613a0e565b01356103ec6103e56103db868b8a613a0e565b6040810190613955565b36916117d4565b916139b3565b6103fc82896114ca565b5261040781886114ca565b5001610380565b9181601f8401121561015d578235916001600160401b03831161015d576020838186019501011161015d57565b606060031982011261015d5760043561045381610193565b9160243591604435906001600160401b03821161015d576104769160040161040e565b9091565b90602061048b9281815201906102b4565b90565b6104973661043b565b919283151580610686575b8061066d575b610655576104da6104d3826104bc33611f3f565b9060018060a01b0316600052602052604060002090565b5460ff1690565b15610649576104f96104ef826104bc33611f3f565b5460081c60ff1690565b8015610641575b8015610604575b156105e15760036105bc6105d5936102466105c29461025d986105756338997b1160401b6001600160601b03193360601b16179461056f61054f61054961213b565b88614f64565b9590946103ec61056561056061213b565b614ee2565b95909f36916117d4565b9a6151e4565b6338997b1160e01b600052600080516020615d4a8339815191526020526105d06105c260077f366b46b479d417a249e7f56f296f035e13c924e69b7ed63bca6e286fe8e383b15b01615c94565b6105ca611983565b906151e4565b6151e4565b6040519182918261047a565b91610600916040519485946320238ecf60e21b86523360048701613a30565b0390fd5b5061063c6104d3600161061a846104bc33611f3f565b016106258686613987565b63ffffffff60e01b16600052602052604060002090565b610507565b508215610500565b61063c6104d333611f06565b60405163171b202760e11b8152336004820152602490fd5b5061068161067d6104ef33611f06565b1590565b6104a8565b503484116104a2565b600091031261015d57565b3461015d57600036600319011261015d576106b3615bf8565b805160005b81811061070c5782604051809160208083018184528251809152816040850193019160005b8281106106ec57505050500390f35b83516001600160a01b0316855286955093810193928101926001016106dd565b80610719600192856114ca565b5160601c61072782866114ca565b52016106b8565b3461015d5760031960603682011261015d576004356001600160401b03811161015d576101608160040192823603011261015d57604435907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361085a5760640160046107a58285613955565b90501061081c57826107c66107c06107ea9361025d96613955565b906152dc565b9060016107d28361141d565b01549060ff8260a81c16926024359260581b90614940565b9080610802575b506040519081529081906020820190565b600080808093338219f15061081561214e565b50386107f1565b61083261082c6106009285613955565b90613987565b60405163fcfc5aad60e01b81526001600160e01b031990911660048201529081906024820190565b60405163ea800da560e01b8152600490fd5b604036600319011261015d5760043561088481610193565b6024356001600160401b03811161015d576108a390369060040161040e565b9091307f00000000000000000000000000000000000000000000000000000000000000001461098a576108d4614899565b9160018060a01b03166352d1902d6001527f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc9384602060016004601d865afa510361097c578082600096817fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b8980a255610957575b846109548585614903565b80f35b908185926040519788378638925af415610972578380610949565b50503d90823e3d90fd5b6355299b496001526004601dfd5b639f03a0266000526004601cfd5b3461015d57600036600319011261015d57307f00000000000000000000000000000000000000000000000000000000000000000361098a5760206040517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152f35b3461015d57602036600319011261015d5761025d610251600561024b600435610a228161014b565b61141d565b3461015d57608036600319011261015d57600435610a4481610193565b6001600160401b039060243582811161015d57610a6590369060040161040e565b9260443581811161015d57610a7e90369060040161040e565b9160643590811161015d57610a97903690600401610261565b929091610aa2614899565b969095610aad613a61565b6001600160a01b03831681529860009181610b6e575b505015610af9575b5090610ae99392918760606100249901805115610aee575b50613cbe565b614903565b600019905238610ae3565b604051630c77631360e41b81529493929190600090869060049082906001600160a01b03165afa978815610b695761002498610ae996600091610b48575b506020820152975090919293610acb565b610b63913d8091833e610b5b81836112e2565b810190611d67565b38610b37565b611efa565b610b7a91810190613ae1565b8051805180610baf575b505080610ba1610b9960206040940151151590565b15158c840152565b015160608a01523880610ac3565b610bc3929350602080918301019101611d67565b60208a0152600190604038610b84565b3461015d57602036600319011261015d5761025d600435610bf38161014b565b610c58610c476002610c3f610c066113fe565b94610c108161531c565b15610c97573086525b610a226001610c278361141d565b015460581b60208801906001600160581b0319169052565b015460581b90565b6001600160581b0319166040830152565b6040805182516001600160a01b031681526020808401516001600160581b03199081169183019190915292820151909216908201529081906060820190565b6001600160a01b03610ca88261141d565b54168652610c19565b602036600319011261015d576004356001600160401b03811161015d57610cdc90369060040161040e565b610ce681836152dc565b90610cf1823361569c565b610cfa8161147b565b9182549460ff861615610e3c5790610d11916138c4565b60609160ff839660081c16610e28575b50610d2b8461141d565b805490916001600160a01b038216908115610e065760609660ff889460a01c16610de5575b5090610d5e610d64926138fd565b9061383a565b94610d6d613937565b9515610ddd5791610d86610d99949261025d98946151e4565b805460a81c60ff16610dc8575b506151e4565b805460101c60ff16610db3575b506040519182918261047a565b6105c26003610dc29201615c94565b38610da6565b6105c26007610dd79201615c94565b38610d93565b855160208701fd5b81610d6493929850610d5e9450610dfb91614f4a565b939097919250610d50565b60405163fcfc5aad60e01b81526001600160e01b031988166004820152602490fd5b819550610e359250614f64565b9338610d21565b60405163742f979f60e11b81523360048201526001600160e01b031986166024820152604490fd5b3461015d5760a08060031936011261015d57600435610e8281610193565b6001600160401b039060443582811161015d57610ea390369060040161040e565b91909260643581811161015d57610ebe903690600401610261565b60849391933583811161015d57610ed9903690600401610261565b93610eef610ee5614899565b99909836916117d4565b94610ef98461133c565b96604090610f098251998a6112e2565b858952602095868a019060051b82019136831161015d57905b82821061102f57505050610f358261133c565b9a610f4282519c8d6112e2565b828c52858c019260051b85019436861161015d5780935b868510610f76576100248d8f610ae98f918f8f8f60243590612279565b843586811161015d5782018036039160c0831261015d578551610f988161127d565b8235610fa381610193565b81528a830135610fb28161014b565b8b82015260608094603f19011261015d578651610fce81611242565b87840135610fdb81611bdb565b815284840135610fea81611af6565b8c8201526080840135610ffc81611bdb565b8882015287820152858301359389851161015d576110208c9594869536910161180b565b90820152815201940193610f59565b81356001600160581b03198116810361015d578152908701908701610f22565b3461015d57600036600319011261015d576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b61025d6110c06110c66110a63661043b565b94916103ec6110b6949294614899565b95909736916117d4565b92614903565b6040519182916020835260208301906102b4565b90815180825260208080930193019160005b8281106110fa575050505090565b83516001600160581b031916855293810193928101926001016110ec565b3461015d57602036600319011261015d5761116a61113b600435610a228161014b565b61025d611156600461114f60038501615c94565b9301615c94565b6040519384936040855260408501906110da565b9083820360208501526110da565b3461015d5760008060031936011261122957604051631aab3f0d60e11b8152306004820152602481018290526020816044817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115610b695782916111ef575b604051828152602090f35b90506020813d8211611221575b81611209602093836112e2565b8101031261121d5761025d915051386111e4565b5080fd5b3d91506111fc565b80fd5b634e487b7160e01b600052604160045260246000fd5b606081019081106001600160401b0382111761125d57604052565b61122c565b604081019081106001600160401b0382111761125d57604052565b608081019081106001600160401b0382111761125d57604052565b6001600160401b03811161125d57604052565b6101a081019081106001600160401b0382111761125d57604052565b602081019081106001600160401b0382111761125d57604052565b90601f801991011681019081106001600160401b0382111761125d57604052565b604051906101af826112ab565b6040519060a082018281106001600160401b0382111761125d57604052565b604051906101af82611242565b6001600160401b03811161125d5760051b60200190565b3461015d57604036600319011261015d576001600160401b0360043581811161015d573660238201121561015d57806004013561138f8161133c565b9161139d60405193846112e2565b81835260209160248385019160051b8301019136831161015d57602401905b8282106113e5576024358587821161015d576113df61002492369060040161040e565b916116fa565b83809183356113f381610193565b8152019101906113bc565b6040519061140b82611242565b60006040838281528260208201520152565b63ffffffff60e01b16600052600080516020615d4a833981519152602052604060002090565b63ffffffff60e01b166000527fade46bbfcf6f898a43d541e42556d456ca0bf9b326df8debc0f29d3f811a0306602052604060002090565b6001600160401b0319166000527fade46bbfcf6f898a43d541e42556d456ca0bf9b326df8debc0f29d3f811a0304602052604060002090565b634e487b7160e01b600052603260045260246000fd5b80518210156114de5760209160051b010190565b6114b4565b906114ed8261133c565b6040906114fc825191826112e2565b838152809361150d601f199161133c565b0191600090815b848110611522575050505050565b602090825161153081611262565b848152828581830152828701015201611514565b906001600160581b031916600052602052604060002090565b9061156782615c94565b9161157460028201615c94565b8351815191906000835b8282106116c85761158f91506114e3565b95600094855b8381106115ff5750505050906000915b8183106115b3575050508252565b9091926115f16115d46115c686856114ca565b516001600160581b03191690565b60206115e0848a6114ca565b5101906001600160581b0319169052565b6001809101930191906115a5565b60019061162261161d6116156115c684886114ca565b848601611544565b615c94565b8051908b82156116a05750906000915b8c82841061164557505050505b01611595565b8b611696879394959d9260208f86956116906115c68f9361168a6116708f6115c6906115e0986114ca565b61167a868a6114ca565b51906001600160581b0319169052565b8b6114ca565b936114ca565b019a019190611632565b8a92506116c1915061167a849b6116bb6115c688978b6114ca565b926114ca565b019661163f565b6001906116f16116eb6116de6115c6868d6114ca565b6001600160581b03191690565b886157aa565b0191019061157e565b91600080516020615d2a8339815191529182549360ff8560081c1615948580966117ac575b8015611795575b156117845760ff1916600117909355611743928461176357611996565b61174957565b600080516020615d2a833981519152805461ff0019169055565b600080516020615d2a833981519152805461ff001916610100179055611996565b60405162dc149f60e41b8152600490fd5b50303b1580156117265750600160ff821614611726565b50600160ff82161061171f565b6001600160401b03811161125d57601f01601f191660200190565b9291926117e0826117b9565b916117ee60405193846112e2565b82948184528183011161015d578281602093846000960137010152565b9080601f8301121561015d5781602061048b933591016117d4565b81601f8201121561015d578035916020916118408461133c565b9361184e60405195866112e2565b808552838086019160051b8301019280841161015d57848301915b8483106118795750505050505090565b82356001600160401b03811161015d57869161189a8484809489010161180b565b815201920191611869565b91909160408184031261015d576001600160401b0392813584811161015d5782019381601f8601121561015d5760209480356118e08161133c565b916118ee60405193846112e2565b818352878084019260051b8201019185831161015d5788809201905b83821061192a57505050509483013590811161015d5761048b9201611826565b8135815290820190820161190a565b604051611945816112c7565b60008152906000368137565b9061195b8261133c565b61196860405191826112e2565b8281528092611979601f199161133c565b0190602036910137565b60405161198f816112c7565b6000815290565b9291906119a5918101906118a5565b83519180518314801590611a82575b611a70576119c0611939565b906119c9611983565b9260005b858110611a2c57505050505050905060018060a01b037f0000000000000000000000000000000000000000000000000000000000000000167f9f08b8dca66d3393166c297eebdbe382963a15cce40f3a2f4bf32378553fe65a600080a2565b80611a6a8686611a4e611a416001968e6114ca565b516001600160a01b031690565b611a5885896114ca565b51611a6386896114ca565b5191612279565b016119cd565b60405163512509d360e11b8152600490fd5b5081518314156119b4565b9080601f8301121561015d57815190602091611aa88161133c565b93611ab660405195866112e2565b818552838086019260051b82010192831161015d578301905b828210611add575050505090565b8380918351611aeb8161014b565b815201910190611acf565b8015150361015d57565b51906101af82611af6565b81601f8201121561015d57805191602091611b258461133c565b93604092611b35845196876112e2565b818652848087019260051b8401019381851161015d57858401925b858410611b61575050505050505090565b83516001600160401b039081811161015d57860191606080601f19858803011261015d57845190611b9182611242565b8a850151611b9e81610193565b825285850151611bad81611af6565b8b83015284015192831161015d57611bcc868b80969581960101611a8d565b85820152815201930192611b50565b60ff81160361015d57565b919082606091031261015d57604051611bfe81611242565b80928051600581101561015d57604091829184526020810151611c2081611bdb565b60208501520151910152565b9080601f8301121561015d578151916020611c468461133c565b93604093611c56855196876112e2565b818652828087019260071b8501019381851161015d578301915b848310611c805750505050505090565b60808383031261015d57836080918751611c9981611262565b8551611ca48161014b565b8152611cb285848801611be6565b83820152815201920191611c70565b81601f8201121561015d578051906020611cda8361133c565b93604090611cea825196876112e2565b848652828601918360e08097028601019482861161015d578401925b858410611d17575050505050505090565b868484031261015d578487918351611d2e81611242565b8651611d398161014b565b8152611d4786848901611be6565b83820152611d588660808901611be6565b85820152815201930192611d06565b60208183031261015d5780516001600160401b039182821161015d57016101a08184031261015d57611d97611303565b92815183811161015d5781611dad918401611a8d565b8452602082015183811161015d5781611dc7918401611a8d565b6020850152604082015183811161015d5781611de4918401611a8d565b6040850152606082015183811161015d5781611e01918401611a8d565b6060850152611e1260808301611b00565b6080850152611e2360a08301611b00565b60a085015260c082015183811161015d5781611e40918401611b0b565b60c085015260e082015183811161015d5781611e5d918401611c2c565b60e08501526101008083015184811161015d5782611e7c918501611c2c565b908501526101208083015184811161015d5782611e9a918501611c2c565b908501526101408083015184811161015d5782611eb8918501611c2c565b908501526101608083015184811161015d5782611ed6918501611cc1565b90850152610180928383015190811161015d57611ef39201611cc1565b9082015290565b6040513d6000823e3d90fd5b6001600160a01b031660009081527fade46bbfcf6f898a43d541e42556d456ca0bf9b326df8debc0f29d3f811a03026020526040902090565b6001600160a01b031660009081527fade46bbfcf6f898a43d541e42556d456ca0bf9b326df8debc0f29d3f811a03056020526040902090565b634e487b7160e01b600052601160045260246000fd5b9060018201809211611f9c57565b611f78565b8151916001600160401b03831161125d57600160401b831161125d578154838355808410611fff575b50602080910191600052806000209060005b848110611fea575050505050565b835160581c8382015592810192600101611fdc565b6000838152846020822092830192015b82811061201d575050611fca565b81815560010161200f565b80548210156114de5760005260206000200190600090565b906120df57815181546001600160a01b0319166001600160a01b03919091161781556101af916120c59060809060208101518454604080840151606085015165ffffffffffff60a01b199093169390911c63ffffffff60a01b169290921760c09290921b60ff60c01b169190911790151560c81b60ff60c81b16178455015160ff1690565b815460ff60d01b191660d09190911b60ff60d01b16179055565b634e487b7160e01b600052600060045260246000fd5b61212e61048b949360a093600180861b03168352602083019060ff60408092828151168552602081015115156020860152015116910152565b81608082015201906102b4565b60405190612148826112c7565b60008252565b3d15612179573d9061215f826117b9565b9161216d60405193846112e2565b82523d6000602084013e565b606090565b6001600160a01b03909116815260406020820181905261048b929101906102b4565b909160609282526121bb6020918483850152848401906110da565b604092838183039101528451928382528282019083808660051b8501019701956000935b8685106121f157505050505050505090565b90919293949596978680612267600193601f198682030189528c518760c091878060a01b03815116845263ffffffff60e01b8682015116868501526122578b8201518c86019060ff60408092828151168552602081015115156020860152015116910152565b0151918160a082015201906102b4565b9a0195019501939695949291906121df565b949390929161229761067d6001600160601b03198860601b1661585c565b612c34576122a761067d87612c55565b612c1357604051630c77631360e41b81526000816004816001600160a01b038b165afa908115610b6957600091612bf8575b506122e761067d868361338f565b612be6578351602082019081515103612bb157845160005b818110612b405750505084600161231589611f06565b015561232c8460026123268a611f06565b01611fa1565b60a0810151612b20575b604081018051519060005b828110612b0057505050606081018051519060005b828110612ae0575050506080810151156129fc5761238361237688611f06565b805460ff19166001179055565b959493929190955b815180600461239989611f06565b015560005b81811061285b57505060e08101515160005b8181106128145750506101008101515160005b8181106127c45750506101208101515160005b81811061277c5750506101408101515160005b81811061272c5750506101608101515160005b8181106126cf5750506101808101515160005b81811061264d5750508051519060005b828110612606575050508051956000965b80881061250d57509495509293919290916001600160a01b0386163b1561015d57600061247191604051809381926306d61fe760e41b83526004830161047a565b0381836001600160a01b038b165af190816124f4575b506124b2578461249561214e565b60405163e838e76160e01b8152918291610600916004840161217e565b936124ef7ffd771bb87e415bdacaa78caa524a7e993befbcdda24e89f4da861ecd9410c05993949560405193849360018060a01b031696846121a0565b0390a2565b8061250161250792611298565b8061068f565b38612487565b61251788846114ca565b51805190989061253d90612531906001600160a01b031681565b6001600160a01b031690565b60408a0151606061254e84886114ca565b510151823b1561015d5761257e92600092838d60405196879586948593630fe8486760e31b8552600485016120f5565b03925af190816125f3575b506125c457886125a861259a61214e565b91516001600160a01b031690565b61060060405192839263a8a545cd60e01b84526004840161217e565b60019192939495969798506125d7611939565b60606125e383886114ca565b5101520196959493929190612430565b8061250161260092611298565b38612589565b8061263461262f612621600194869d98999a9b9c9d516114ca565b516001600160e01b03191690565b611443565b61263e8154611f8e565b9055019796959493929761241f565b80610180849a95969798999a015190612665916114ca565b51516001600160e01b0319168980888461018088015190612685916114ca565b51602001519161269492613612565b818985610180890151906126a7916114ca565b5160400151916126b6926134e5565b916126c093612fe5565b6001019796959493929761240f565b8061271f896126ec600194610160889e999a9b9c9d9e01516114ca565b518051612719908b906001600160e01b03191693604061271183836020880151613612565b9401516134e5565b91612f3d565b01979695949392976123fc565b8061276f612748600193610140879d98999a9b9c9d01516114ca565b51612769898d6020612761855163ffffffff60e01b1690565b940151613612565b90613176565b01979695949392976123e9565b806127b7612798600193610120879d98999a9b9c9d01516114ca565b516127b1898d6020612761855163ffffffff60e01b1690565b90613116565b01979695949392976123d6565b806128076127e0600193610100879d98999a9b9c9d01516114ca565b51612801898d60206127f9855163ffffffff60e01b1690565b940151613590565b90612edb565b01979695949392976123c3565b8061284e61282f60019360e0879d98999a9b9c9d01516114ca565b51612848898d6020612711855163ffffffff60e01b1690565b90612e79565b01979695949392976123b0565b61286b8185969798999a956114ca565b5180516001600160a01b031660208201516001600160e01b0319166040830151805160ff166020820151151560409092015160ff16926128a9611310565b6001600160a01b0390951685526001600160e01b031916602085015260ff1660408401521515606083015260ff166080820152826128e68c611f06565b600401906128f391612028565b6128fd9291612040565b80516001600160a01b031661291190611f06565b600301805461291f90611f8e565b9055805160601b6001600160601b0319166000908152600080516020615d6a8339815191526020526040902054156129d9576020810151600192916129c2916001600160e01b03191681519091908d906040906001600160a01b031692015161299261298c825160ff1690565b846157f9565b926129a06020830151151590565b156129cf576129b660406129bc93015160ff1690565b906157f9565b92612fe5565b019796959493929761239e565b5050600092612fe5565b51604051639ca2818b60e01b81526001600160a01b039091166004820152602490fd5b60c08101515160005b818110612a1a5750509594939291909561238b565b612a288160c08501516114ca565b51612a5b612a3e6001600160a01b038c16611f3f565b82516001600160a01b031660009081526020919091526040902090565b805460ff1916600117815590602081015115612a895750805461ff0019166101001790556001905b01612a05565b604001805151916001019060005b838110612aaa5750505050600190612a83565b80612ada612376612ac161262160019587516114ca565b869063ffffffff60e01b16600052602052604060002090565b01612a97565b80612afa8b612af561262160019587516114ca565b612f98565b01612356565b80612b1a8b612b1561262160019587516114ca565b612d99565b01612341565b612b3b612b2c88611f06565b805461ff001916610100179055565b612336565b612b5f612b506115c6838a6114ca565b9060ff8260601c9260581c1690565b509060019182612b6e82611f06565b015415612bc357612b8f61067d612b896126218589516114ca565b83612ce0565b612bb157612b9e600391611f06565b01612ba98154611f8e565b9055016122ff565b604051631794e5f160e01b8152600490fd5b604051639ca2818b60e01b81526001600160a01b03919091166004820152602490fd5b604051635f1b910f60e11b8152600490fd5b612c0d91503d806000833e610b5b81836112e2565b386122d9565b60405163b7038f3960e01b81526001600160a01b0387166004820152602490fd5b604051639d615d0560e01b81526001600160a01b0387166004820152602490fd5b612c5e81612d40565b9081612cce575b81612c6e575090565b60209150600090604051838101906301ffc9a760e01b825263ea15602d60e01b602482015260248152612ca081611242565b5191617530fa6000513d82612cc2575b5081612cbb575b5090565b9050151590565b60201115915038612cb0565b9050612cd981612d69565b1590612c65565b612ce981612d40565b9182612d2e575b82612cfa57505090565b6020925090600091604051848101916301ffc9a760e01b835263ffffffff60e01b16602482015260248152612ca081611242565b9150612d3981612d69565b1591612cf0565b6000602091604051838101906301ffc9a760e01b808352602482015260248152612ca081611242565b6000602091604051838101906301ffc9a760e01b825263ffffffff60e01b602482015260248152612ca081611242565b9190612da48361141d565b80546001600160a01b0316612e3257612dbc8461531c565b612e1057612dc9846154c8565b612dee5780546001600160a01b0319166001600160a01b039092169190911790559050565b604051633cecfc3760e01b81526001600160e01b031985166004820152602490fd5b60405163e171c77960e01b81526001600160e01b031985166004820152602490fd5b60405163ec9cbcb360e01b81526001600160e01b031985166004820152602490fd5b6001600160e01b031990911681526001600160581b0319909116602082015260400190565b612e82826136c5565b6001612e8d8261141d565b01906001600160581b0319825460581b16612ebd575080546001600160a81b03191660589290921c919091179055565b604051632caa037760e21b8152928392506106009160048401612e54565b612ee4826136c5565b6002612eef8261141d565b01906001600160581b0319825460581b16612f1f575080546001600160a81b03191660589290921c919091179055565b60405163e709805160e01b8152928392506106009160048401612e54565b91612f568183612f4c8661141d565b956005870161303f565b6001600160581b0319918216612f82575b16612f6f5750565b805460ff60a81b1916600160a81b179055565b825460ff60a01b1916600160a01b178355612f67565b612fab906001600160401b03199261569c565b166000527fade46bbfcf6f898a43d541e42556d456ca0bf9b326df8debc0f29d3f811a03046020526040600020600160ff19825416179055565b92908183612ff9610246876130039561569c565b956001870161303f565b6001600160581b031991821661302d575b1661301c5750565b805462ff0000191662010000179055565b825461ff001916610100178355613014565b9091906001600160581b0319808316919082156130e05761306361067d84846156bc565b6130c45785169081613078575b505050505050565b61309b9360018261308f61067d9661309695615b61565b5001611544565b6156bc565b6130a85780808080613070565b6106006040519283926320fdf93b60e21b845260048401612e54565b6040516320fdf93b60e21b815280610600868860048401612e54565b8516925090508115613104576130fc91600261067d92016156bc565b6130a8575050565b60405163036488f560e51b8152600490fd5b90613120816136c5565b6131298261141d565b916131416001600160581b03198316600385016156bc565b1561315b57505060010160ff815460a81c1615612f6f5750565b61060060405192839262c3580d60e61b845260048401612e54565b90613180816136c5565b6131898261141d565b916131a16001600160581b03198316600485016156bc565b156131bb57505060020160ff815460a81c1615612f6f5750565b610600604051928392630766bae360e51b845260048401612e54565b90815180825260208080930193019160005b8281106131f7575050505090565b83516001600160e01b031916855293810193928101926001016131e9565b90815180825260208092019182818360051b82019501936000915b8483106132405750505050505090565b909192939495848061328083856001950387528a5190606090868060a01b03835116815284830151151585820152816040809401519382015201906131d7565b9801930193019194939290613230565b6005111561329a57565b634e487b7160e01b600052602160045260246000fd5b8051600581101561329a576040918291845260ff60208201511660208501520151910152565b90815180825260208080930193019160005b8281106132f6575050505090565b9091929382608060019261331e83895163ffffffff60e01b81511684520151848301906132b0565b019501939291016132e8565b90815180825260208080930193019160005b82811061334a575050505090565b909192938260e06001926133836040895163ffffffff851b815116845261337786820151878601906132b0565b015160808301906132b0565b0195019392910161333c565b6040516134de816134d060208201946020865280516133bc6101a0918260408701526101e08601906131d7565b906134bd61340a6133f66133e2602087015195603f1996878b83030160608c01526131d7565b6040870151868a83030160808b01526131d7565b6060860151858983030160a08a01526131d7565b6080850151151560c08801529160a0850151151560e088015261343e60c08601519361010094868a830301868b0152613215565b906134aa61349361347c61346460e08a015195610120968d888c828503019101526132d6565b9689015196610140978c898b828503019101526132d6565b938801519361016094888c830301868d01526132d6565b948701519461018095878b830301878c01526132d6565b918601519085898403019089015261332a565b92015190848303016101c085015261332a565b03601f1981018352826112e2565b5190201490565b91600183516134f381613290565b6134fc81613290565b0361351357506129b6602061048b93015160ff1690565b90506002825161352281613290565b61352b81613290565b03613542576115c690604061048b930151906114ca565b506003815161355081613290565b61355981613290565b0361357057604051635f1b910f60e11b8152600490fd5b6004905161357d81613290565b61358681613290565b14612be657600090565b916001835161359e81613290565b6135a781613290565b036135be57506129b6602061048b93015160ff1690565b9050600282516135cd81613290565b6135d681613290565b036135ed576115c690604061048b930151906114ca565b50600381516135fb81613290565b61360481613290565b036135705750600160581b90565b916001835161362081613290565b61362981613290565b0361364057506129b6602061048b93015160ff1690565b90506002825161364f81613290565b61365881613290565b0361366f576115c690604061048b930151906114ca565b506003815161367d81613290565b61368681613290565b0361369d57604051635f1b910f60e11b8152600490fd5b600490516136aa81613290565b6136b381613290565b146136bd57600090565b600160591b90565b6001600160581b0319161561310457565b906136e08261133c565b6136ed60405191826112e2565b82815280926136fe601f199161133c565b019060005b82811061370f57505050565b806060602080938501015201613703565b6000356001600160e01b0319166137368161141d565b80546001600160a01b03166001600160a01b0381811615613818577f00000000000000000000000000000000000000000000000000000000000000001633141560001461380957613785614c3a565b925b825491606094859260ff8560a01c166137e8575b5090610d5e6137a9926138fd565b936137b2613937565b94156137e05760ff92916137c5916151e4565b60a81c166137d1575090565b6105c2600761048b9201615c94565b845160208601fd5b816137a993929750610d5e94506137fe91614f4a565b93909691925061379b565b6138123661386d565b92613787565b60405163fcfc5aad60e01b81526001600160e01b031985166004820152602490fd5b600091829182602083519301915af190565b908060209392818452848401376000828201840152601f01601f1916010190565b61048b60c460405180936000602083015260006024830152336044830152346064830152608060848301528060a4830152806000848401376000838284010152601f801991011681010360a48101845201826112e2565b9061048b906134d060405193849260006020850152600060248501523360448501523460648501526080608485015260a484019161384c565b8051613931575061390d366117b9565b61391a60405191826112e2565b368152366000602083013760006020368301015290565b60a40190565b604051903d8252601f19603f3d840101166040523d6000602084013e565b903590601e198136030182121561015d57018035906001600160401b03821161015d5760200191813603831361015d57565b6001600160e01b031990358181169392600481106139a457505050565b60040360031b82901b16169150565b9291906139bf84612c55565b6139ed5790600092938392602083519301915af1906139dc61214e565b91156139e457565b50602081519101fd5b604051637d03783760e11b81526001600160a01b0385166004820152602490fd5b91908110156114de5760051b81013590605e198136030182121561015d570190565b6001600160a01b03918216815291166020820152604081019190915260806060820181905261048b9391019161384c565b60405190613a6e8261127d565b600080835260405183613a80826112ab565b60609182815282602082015282604082015282808201528360808201528360a08201528260c08201528260e0820152826101008201528261012082015282610140820152826101608201528261018082015260208201528260408201520152565b9060208282031261015d5781356001600160401b039283821161015d570160608183031261015d5760405192613b1684611242565b813590811161015d57604092613b2d91830161180b565b83526020810135613b3d81611af6565b60208401520135604082015290565b9060405191828154918282526020928383019160005283600020936000905b828210613b81575050506101af925003836112e2565b855460581b6001600160581b03191684526001958601958895509381019390910190613b6b565b600019810191908211611f9c57565b9060405160a081018181106001600160401b0382111761125d57604052608060ff82945460018060a01b038116845263ffffffff60e01b8160401b166020850152818160c01c166040850152818160c81c161515606085015260d01c16910152565b908210156114de576104769160051b810190613955565b600080825560019181838201556002810180549083815581613c89575b5050808260036004930155019081549181815582613c6c575b50505050565b815260208120918201915b82811015613c66578181558301613c77565b83528360208420918201915b828110613ca25750613c4d565b848155018490613c95565b91602061048b93818152019161384c565b9290949394613cf161067d613cec613cdc875160018060a01b031690565b60601b6001600160601b03191690565b6159b5565b61468757602091828501938451613d2861067d60019283613d20613d1b8c5160018060a01b031690565b611f06565b01549061338f565b612be6578651600395908690613d46906001600160a01b0316611f06565b0154614663578751600290613d6f908290613d69906001600160a01b0316611f06565b01613b4c565b80518460005b82811061463457505050610180808a5101515190858b8d876000925b878785106145b5575050509350505050610160858b8487848351015151956000935b878510614558575095505050505050610140858b84838251015151946000925b868410614532575050505050505061012090818a5101515191858c8c6000915b8683106144df575050505050505061010090818951015151918460005b8b8582106144bf5750505050505060e0808851015151908360005b8a84821061447f5793505050505193608094613e4986820151151590565b156143ca57508851613e7190613e67906001600160a01b0316611f06565b805460ff19169055565b8851600497908890613e8b906001600160a01b0316611f06565b0154908460005b8a8d858310614302575050505050506060948589510151518460005b8b8d8a8584106142c9575050505050506040988981510151518560005b8c83821061429a5750505050805151519060005b82811061427457505050899a9b88613f05613d1b879d9b9c9d9b5160018060a01b031690565b01549381151591828061426a575b61425a579594939291906000965b8588106140825750508c51613f4f9650613f4a95506001600160a01b03169350613d1b92505050565b613c30565b8651613f6590612531906001600160a01b031681565b91870151823b1561015d57613f9293600080948951809781968295638a91b0e360e01b84528c8401613cad565b0393f1908161406f575b5061405f5750613faa61214e565b613fb961067d84860151151590565b614039575050815160009261400e925061253191613fdd906001600160a01b031683565b7fd7b55aedaf176e219cf7181e0fd7055f9066797839ebac8eaf0e7891226f83138580a2516001600160a01b031690565b901515907feb7551bad8fd10038dee62a958c2b6f45624499dc800ff8936bb0a4904bdd2fe600080a3565b835161060091906001600160a01b03169351631ad8069f60e21b8152938493840161217e565b9261400e92506125319150611a41565b8061250161407c92611298565b38613f9c565b909192939495969e9d9c8f9b8c8f9e839f819f90898f926140ba6140c091866140b4613d1b875160018060a01b031690565b01612028565b50613bb7565b96888d6140d96125316125318c5160018060a01b031690565b976141438d61413a8d61411c6140f98c8c01519b5160018060a01b031690565b9961411361410a8b85015160ff1690565b9d840151151590565b92015160ff1690565b9261413161412861132f565b60ff909d168d52565b8b019015159052565b60ff1688860152565b8d1561424957614156926103e592613c19565b955b803b1561015d57600095614180879351988997889687946333b61ee160e11b865285016120f5565b0393f19081614236575b5061422957508d6141a861067d8f6141a061214e565b930151151590565b6141ff57508d5190519e9f9d9e9c9d9b9c60009c899290916001600160a01b0390811691167fbbdf38e3835b5c2965aa65f15bb4bef6b629d58bc7eade21276b661b136b3bbd8f80a35b0196959493929190613f21565b81518e918e9161060091906001600160a01b031693516334f2a43f60e11b8152938493840161217e565b9b9c9d9e9f8891506141f2565b8061250161424392611298565b3861418a565b50505061425461213b565b95614158565b8c5163512509d360e11b81528c90fd5b5085811415613f13565b8061428861262f6126218a948651516114ca565b6142928154613ba8565b905501613edf565b6142b1610a22612621846142c194895101516114ca565b80546001600160a01b0319169055565b018690613ecb565b6142fa926142f461262186610246946142eb613e67975160018060a01b031690565b945101516114ca565b9061569c565b018590613eae565b80516143b892614324916140ba9186916140b4906001600160a01b0316611f06565b8051869061433a906001600160a01b0316611f06565b016143458154613ba8565b905580890151614367906001600160e01b03191692516001600160a01b031690565b81518d906001600160a01b031661438b614385604086015160ff1690565b826157f9565b936143996060820151151590565b600090156143c057506143b2926129b691015160ff1690565b926146ec565b018590613e92565b92505050926146ec565b60c08091015151908460005b8c8c8583106143ea57505050505050613e71565b612a3e6144136125316144048689614418965101516114ca565b5194516001600160a01b031690565b611f3f565b805460ff1916815590808801511561443d5750805461ff00191690555b0185906143d6565b6040018051519184019060005b83811061445a5750505050614435565b614473613e67612ac161262184869798999a96516114ca565b0190899493929161444a565b826144a6610a2261449785886144b7965101516114ca565b51516001600160e01b03191690565b0180546001600160a81b0319169055565b018490613e2b565b846144a6610a2261449785886144d7965101516114ca565b018590613e10565b614522866144f48588614528965101516114ca565b519261451c8c61450b865163ffffffff60e01b1690565b95015191516001600160a01b031690565b90613612565b906147e5565b0186908c8e613df3565b61454d926144f48588614547945101516114ca565b90614821565b018690848e8e613dd3565b6145a99361456d86896145a3945101516114ca565b519261459d604061450b84614589885163ffffffff60e01b1690565b9888015185516001600160a01b031661451c565b906134e5565b916146aa565b01869084878f8f613db3565b6143b2816146299561459d60406146188a8d61460f8a996145dd61449785858e5101516114ca565b9c6145fc85856145f38c5160018060a01b031690565b9e5101516114ca565b51015188516001600160a01b031661451c565b995101516114ca565b51015191516001600160a01b031690565b0186908d878e613d91565b8a61464e614648612b506115c685896114ca565b50611f06565b016146598154613ba8565b9055018590613d75565b8751604051632b4ecd8d60e21b81526001600160a01b039091166004820152602490fd5b83516040516271c02f60e31b81526001600160a01b039091166004820152602490fd5b916146b86146c2929361141d565b926005840161472f565b906146dc575b6146cf5750565b805460ff60a81b19169055565b815460ff60a01b191682556146c8565b6102466146fe9161470894959361569c565b926001840161472f565b90614721575b6147155750565b805462ff000019169055565b815461ff001916825561470e565b600093849390926001600160581b03198082169283156147be576147538486615737565b5061475d85615bd5565b6147b5575b168061476e5750505050565b614798916147939161478d60018701916147888484611544565b615737565b50611544565b615bd5565b6147a4575b8080613c66565b6147ad91615b9f565b50388061479d565b60019750614762565b6147d99460020193506147d392501682615737565b50615bd5565b6147df57565b60019150565b6147f161480a9161141d565b916147d360038401916001600160581b03191682615737565b6148115750565b600101805460ff60a81b19169055565b61482d6148469161141d565b916147d360048401916001600160581b03191682615737565b61484d5750565b600201805460ff60a81b19169055565b6001600160e01b0319818116908114614892576301ffc9a760e01b1461488c5761488690611443565b54151590565b50600190565b5050600090565b6040516148a5816112c7565b600081527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036148f0575b610476906000356001600160e01b031916614f4a565b506104766148fc614c3a565b90506148da565b9061490d916151e4565b6101af61492a60076105bc6000356001600160e01b03191661141d565b60405190614937826112c7565b600082526151e4565b60009390926001600160581b0319831615614a9c579061495f91614b15565b916000946149b4575b506020820180516001600160e01b031663af87348360e01b1790526149a49161499f9161499490612b50565b60ff16602483015291565b614abe565b9115612cb7579061048b916155e2565b6149c59194506105bc60039161141d565b928351936000905b8582106149da5750614968565b9093614a026149ec6115c687856114ca565b600160591b6001600160581b0319909116111590565b614a8a57614a16612b506115c687856114ca565b60ff811660248701529091614a2b8387614abe565b6001939092906001600160a01b038416858111614a575750505090614a4f9161553c565b9401906149cd565b60405163332a984f60e11b81526001600160a01b03928316600482015260ff939093166024840152166044820152606490fd5b604051636756835b60e11b8152600490fd5b604051636596ccb160e01b81526001600160e01b031985166004820152602490fd5b80516020926000928401908390600019f1601f3d111615614adf5760005190565b600190565b9035601e198236030181121561015d5701602081359101916001600160401b03821161015d57813603831361015d57565b604051632238633960e21b602082015260006024820152606060448201529161048b91839190614c2690614b5c60848501614b4f836101a4565b6001600160a01b03169052565b602081013560a4850152614b89614b766040830183614ae4565b61016060c48801526101e487019161384c565b614c16614c0a614bb3614b9f6060860186614ae4565b6083198a8703810160e48c0152959161384c565b608085013561010489015260a085013561012489015260c085013561014489015260e0850135610164890152610100850135610184890152614bf9610120860186614ae4565b90858a8403016101a48b015261384c565b92610140810190614ae4565b91868403016101c487015261384c565b90606483015203601f1981018352826112e2565b6000356001600160e01b03191690614c566002610c3f8461141d565b91614c706002614c658361141d565b015460a81c60ff1690565b92614c7a3661386d565b93614d3c575b6001600160581b0319811690600160591b8211614cfe57501580614cca575b614ca65750565b604051637214fb8b60e11b81526001600160e01b0319919091166004820152602490fd5b506356b07d1360e11b8114801590614ced575b8015614c9f575030331415614c9f565b5063278f794360e11b811415614cdd565b6020850180516001600160e01b031663bfd151c160e01b1790526101af9250614d369150614d2b90612b50565b60ff16602486015290565b83614e38565b6020840180516001600160e01b031663031fb36160e21b1790526004614d65816105bc8561141d565b9081519160005b838110614d7c5750505050614c80565b614d896115c682846114ca565b600160591b6001600160581b031982161115614dd25790614dcc614dc6614dbb6001949060ff8260601c9260581c1690565b60ff1660248d015290565b8a614de3565b01614d6c565b604051636756835b60e11b81528490fd5b60008082516020840182865af115614df9575050565b6024604051916084601f19601f3d011601936340b788e360e01b8452600484015201516024820152606060448201523d60648201523d6000608483013efd5b60008082516020840182865af115614e4e575050565b6024604051916084601f19601f3d011601936310b2d36b60e01b8452600484015201516024820152606060448201523d60648201523d6000608483013efd5b60008082516020840182865af115614ea3575050565b6024604051916084601f19601f3d01160193630cb6620d60e01b8452600484015201516024820152606060448201523d60648201523d6000608483013efd5b6338997b1160e01b600052600080516020615d4a833981519152602052610476907f366b46b479d417a249e7f56f296f035e13c924e69b7ed63bca6e286fe8e383b77f366b46b479d417a249e7f56f296f035e13c924e69b7ed63bca6e286fe8e383b6614fdc565b90614f576104769261141d565b6005600682019101614fdc565b90614f716104769261147b565b6001600282019101614fdc565b60208183031261015d578051906001600160401b03821161015d570181601f8201121561015d578051614fb0816117b9565b92614fbe60405194856112e2565b8184526020828401011161015d5761048b9160208085019101610291565b929091614fe884615c94565b90815190600095865b83811061517e575061500b61500588611951565b976136d6565b9560009484156130705783511561516d575b6020840180516001600160e01b031663236b075960e11b179052855b85811061504d575050505050508084528252565b61505a6115c682846114ca565b6001600160581b0319811690600160591b821115614a8a576150b66150979261508c839060ff8260601c9260581c1690565b60ff1660248b015293565b6004918291899061ffff191660005260205261fffc6040600020541690565b160361515a57906150cb61161d8d9387611544565b8051908c6000945b8386106150ea575050505050506001905b01615039565b9b6151489161511d879e61510e848f6115c68b9c61168a849b9c9d6116bb94614e8d565b906001600160581b0319169052565b615138615128613937565b8051602080918301019101614f7e565b61514283836114ca565b526114ca565b5060018091019a019291908c8e6150d3565b509061516860019287614e8d565b6150e4565b92506151783661386d565b9261501d565b9660019061519b6151956116de6115c68c8a6114ca565b846157aa565b019701614ff1565b60409060ff61048b949316815281602082015201906102b4565b60ff61048b949360609360018060a01b0316835216602082015281604082015201906102b4565b908151815115159091825b6151f95750505050565b90919293615215612b506115c661520f87613ba8565b886114ca565b90956001600160a01b0387169491939180156152ce5761523d61523788613ba8565b846114ca565b51965b863b1561015d57604096875190631128186d60e01b825281808960049c8d83019161526a926151a3565b03815a6000948591f190816152bb575b506152a4578888610600898961528e61214e565b9151631996ddff60e21b815294859485016151bd565b9397509095506000199092019350909150826151ef565b806125016152c892611298565b3861527a565b6152d661213b565b96615240565b9190600481106152f05761048b9192613987565b6152fc60249184613987565b60405163fcfc5aad60e01b81526001600160e01b03199091166004820152fd5b63ffffffff60e01b16633a871cdd60e01b81149081156154b7575b81156154a6575b8115615495575b8115615484575b8115615473575b8115615462575b8115615451575b8115615440575b811561542f575b811561541e575b811561540d575b81156153fc575b81156153eb575b81156153da575b81156153c9575b81156153b8575b81156153aa575090565b631d06562b60e11b14919050565b63ceaf130960e01b811491506153a0565b630b0faea160e01b81149150615399565b63190be77560e21b81149150615392565b632344486160e21b8114915061538b565b631cd3c49560e31b81149150615384565b6338997b1160e01b8114915061537d565b6394ed11e760e01b81149150615376565b631a7e6adf60e11b8114915061536f565b635b0e93fb60e11b81149150615368565b63278f794360e11b81149150615361565b6352d1902d60e01b8114915061535a565b6301ffc9a760e01b81149150615353565b631df68add60e21b8114915061534c565b6356b07d1360e11b81149150615345565b631a10fa5160e31b8114915061533e565b63586b48ff60e11b81149150615337565b63ffffffff60e01b1663e3563a4f60e01b811490811561552b575b811561551a575b8115615509575b81156154fb575090565b63a9a2340960e01b14919050565b637a32e3bf60e11b811491506154f1565b63275e2d7960e01b811491506154ea565b6364c530cd60e01b811491506154e3565b9065ffffffffffff808360a01c1680156155db575b818360a01c169182156155d3575b82811690821611156155bd575060a01b65ffffffffffff60a01b16915b8160d01c8160d01c106000146155ac576001600160d01b03198216915b60018060a01b0380911691161791171790565b6001600160d01b0319811691615599565b60a01b65ffffffffffff60a01b1692905061557c565b91508161555f565b5080615551565b90600165ffffffffffff808460a01c168015615695575b818460a01c1691821561568d575b8281169082161115615677575060a01b65ffffffffffff60a01b16925b8260d01c8160d01c10600014615666576001600160d01b03198316925b828060a01b0392838093161460001461566057505060015b1691171790565b16615659565b6001600160d01b0319811692615641565b60a01b65ffffffffffff60a01b16939050615624565b915081615607565b50806155f9565b60601b6001600160601b03191660a09190911c63ffffffff60401b161790565b6156d882829061ffff1916600052602052604060002054151590565b1561572d576156fb82829061ffff191660005260205261fffc6040600020541690565b9160ff8360081c1660ff81146157245761ff00600160ff61048b9616920160081b161791615b2c565b50505050600090565b9061048b9161590b565b61575382829061ffff1916600052602052604060002054151590565b156148925761577682829061ffff191660005260205261fffc6040600020541690565b9160ff8360081c16801561579e5761ff0060ff61048b9516916000190160081b161791615b2c565b509061048b9250615a61565b6157c682829061ffff1916600052602052604060002054151590565b156148925760ff6157f061ffff936001939061ffff191660005260205261fffc6040600020541690565b60081c16011690565b60601b6001600160601b03191660589190911b60ff60581b161790565b6001600052600080516020615d6a833981519152602052600080516020615d8a83398151915290565b600052600080516020615d6a833981519152602052604060002090565b61ffff1916801580156158e7575b6158e1576001600052600080516020615d6a833981519152602052600080516020615d8a83398151915254801580156158d7575b156158c057506158ba81614adf926158b4615816565b5561583f565b60019055565b908060026158d192176158b4615816565b55600190565b506001811661589e565b50600090565b5080600052600080516020615d6a833981519152602052604060002054151561586a565b9061ffff1916908115801561599f575b6148925760016000528060205260406000205480158015615995575b1561596a57508181614adf9361595a6158ba946001600052602052604060002090565b5590600052602052604060002090565b9160028117615983836001600052602052604060002090565b55600052602052604060002055600190565b5060018116615937565b508160005280602052604060002054151561591b565b61ffff1916906159c48261583f565b5482158015615a59575b615a525760016000815b15615a28575b506000906159eb8161583f565b549061fffe198083169190878314615a05575050916159d8565b6158b49294969750946158d19561fffc600283969496169316911617179161583f565b60018116159081615a48575b5015615a4057386159de565b506000925050565b9050151538615a34565b5060009150565b5080156159ce565b919061ffff191691615a7d838290600052602052604060002090565b549083158015615b24575b615a405760016000815b15615af9575b50600090615ab0818490600052602052604060002090565b549061fffe198083169190888314615aca57505091615a92565b61595a929594979850956158d19661fffc60028396949616931691161717918390600052602052604060002090565b60018116159081615b1a575b5015615b115738615a98565b50600093505050565b9050151538615b05565b508115615a88565b9061ffff19166000526020526040600020908154908115615b595761fffc169061fffc1916179055600190565b505050600090565b615b7f82829061ffff191660005260205261fffc6040600020541690565b91600480841614615b9757600461048b931791615b2c565b505050600190565b615bbd82829061ffff191660005260205261fffc6040600020541690565b916004831615615b975761fffb61048b931691615b2c565b60016000526020526040600020548015908115615bf0575090565b600191501690565b60016000818152600080516020615d6a833981519152602052600080516020615d8a83398151915254604051939290815b615c3d575b50810160051b83016040528252565b918183161580615c8b575b15615c855761fffe198316908201600581901b86018290529282919060021615615c7c57615c759061583f565b5490615c29565b50600090615c29565b91615c2e565b50821515615c48565b9060006001809381835280602052604083205490604051955b615cc2575b5050810160051b83016040528252565b90928284161580615d20575b15615d1a5761fffe198416908301600581901b87018290529383919060021615615d1157615d06908390600052602052604060002090565b54905b919091615cad565b50600090615d09565b92615cb2565b50831515615cce56feade46bbfcf6f898a43d541e42556d456ca0bf9b326df8debc0f29d3f811a0300ade46bbfcf6f898a43d541e42556d456ca0bf9b326df8debc0f29d3f811a0303ade46bbfcf6f898a43d541e42556d456ca0bf9b326df8debc0f29d3f811a030194cfd67f685607e46dacbf75e6a3a5cc7e0a77a9dde0d0269384d8135edf6083a2646970667358221220de70c98538ceda69f90033400de9bce15a8cd998a0ec494ef0e0d9ecd52ee39c64736f6c634300081500330000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d2789", + "nonce": "0x47", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x80c2f62037e084f594ce6d0be7de0119d62e5d9faefe5f903515d1b76d9fea40", + "transactionType": "CREATE", + "contractName": "MultiOwnerPlugin", + "contractAddress": "0x56bC629F342821FBe91C5273880792dFECBE7920", + "function": null, + "arguments": null, + "transaction": { + "type": "0x02", + "from": "0x7f89ed1f3f0d52d303904101305471bca3cde710", + "gas": "0x25836c", + "value": "0x0", + "data": "0x6101a06040818152346200019f576200001882620001a4565b6012825260208201907126bab63a349027bbb732b91028363ab3b4b760711b928383528151906200004982620001a4565b600582526020820191640312e302e360dc1b928381526200006a83620001c0565b956101209687526200007c8362000393565b93610140948552519020918260e05251902090610100968288524660a05285519260208401927f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f84528785015260608401524660808401523060a084015260a0835260c0830183811060018060401b038211176200018957600593601282819560e0948b52835190206080523060c0526200011781620001a4565b52019081522092610160938452602085516200013381620001a4565b83815201908152209261018093845251936120dc958662000541873960805186505060a05186505060c05186505060e051865050518550505184610a1b01525183610a4401525182611953015251816119780152f35b634e487b7160e01b600052604160045260246000fd5b600080fd5b604081019081106001600160401b038211176200018957604052565b805160209190828110156200025f575090601f825111620001fe5780825192015190808310620001ef57501790565b82600019910360031b1b161790565b90604051809263305a27a960e01b82528060048301528251908160248401526000935b82851062000245575050604492506000838284010152601f80199101168101030190fd5b848101820151868601604401529381019385935062000221565b6001600160401b03811162000189576000928354926001938481811c9116801562000388575b838210146200037457601f81116200033e575b5081601f8411600114620002d757509282939183928694620002cb575b50501b916000199060031b1c191617905560ff90565b015192503880620002b5565b919083601f1981168780528488209488905b8883831062000323575050501062000309575b505050811b01905560ff90565b015160001960f88460031b161c19169055388080620002fc565b858701518855909601959485019487935090810190620002e9565b85805284601f848820920160051c820191601f860160051c015b8281106200036857505062000298565b87815501859062000358565b634e487b7160e01b86526022600452602486fd5b90607f169062000285565b805160209081811015620004215750601f825111620003c05780825192015190808310620001ef57501790565b90604051809263305a27a960e01b82528060048301528251908160248401526000935b82851062000407575050604492506000838284010152601f80199101168101030190fd5b8481018201518686016044015293810193859350620003e3565b9192916001600160401b038111620001895760019182548381811c9116801562000535575b828210146200051f57601f8111620004e6575b5080601f8311600114620004995750819293946000926200048d575b5050600019600383901b1c191690821b17905560ff90565b01519050388062000475565b90601f198316958460005282600020926000905b888210620004ce57505083859697106200030957505050811b01905560ff90565b808785968294968601518155019501930190620004ad565b8360005283601f83600020920160051c820191601f850160051c015b8281106200051257505062000459565b6000815501849062000502565b634e487b7160e01b600052602260045260246000fd5b90607f16906200044656fe608080604052600436101561001357600080fd5b60003560e01c90816301ffc9a71461105f575080630c7ecd8414610bc45780630c976227146110385780631128186d14610fe8578063118a538914610de05780631626ba7e14610d90578063197ea35b14610d685780632f54bf6e14610d445780633956224714610be857806346d60eb214610bc4578063676c3dc214610b0e5780636d61fe7014610b135780637f42433814610b0e57806384b0196e14610a055780638616d61d146109de57806388e18ce4146109bb5780638a91b0e314610932578063af87348314610911578063affe39c1146108d8578063bfd151c11461086a578063c7763130146101595763f582bceb1461011157600080fd5b346101545760403660031901126101545761012a6110cc565b602435906001600160a01b03821682036101545760209161014a916119f7565b6040519015158152f35b600080fd5b3461015457600036600319011261015457610172611d2e565b5061017b611d2e565b60405161018781611217565b6005815260a03660208301378060408301526101aa633956224760e01b91611a23565b5263affe39c160e01b6101c06040830151611a46565b526317aa5fb760e11b6101d66040830151611a56565b526342580cb760e11b6101ec6040830151611a66565b52630b135d3f60e11b6102026040830151611a76565b5260405161020f816111fc565b6001815260016020820152600060408201526040518060e08101106001600160401b0360e0830111176108145760e081016040526006815260005b60c0811061082a57509061039c9160e084015261029260405161026c816111e1565b633956224760e01b81526020810183905260e08501519061028c82611a23565b52611a23565b506102c86040516102a2816111e1565b635b0e93fb60e11b81526020810183905260e0850151906102c282611a46565b52611a46565b506102fe6040516102d8816111e1565b631a7e6adf60e11b81526020810183905260e0850151906102f882611a56565b52611a56565b5061033460405161030e816111e1565b6356b07d1360e11b81526020810183905260e08501519061032e82611a66565b52611a66565b5061036a604051610344816111e1565b631df68add60e21b81526020810183905260e08501519061036482611a76565b52611a76565b5060405190610378826111e1565b63278f794360e11b8252602082015260e08301519061039682611a86565b52611a86565b50604051906103aa826111fc565b6001825260006020830152600060408301526040516103c8816111fc565b60038152600060208201526000604082015260405192836101608101106001600160401b0361016086011117610814576101608401604052600a845260005b61014081106107d4575061054190839461010061061995015261045060405161042f816111e1565b633956224760e01b8152602081018390526101008701519061028c82611a23565b50610481604051610460816111e1565b635b0e93fb60e11b815260208101839052610100870151906102c282611a46565b506104b2604051610491816111e1565b631a7e6adf60e11b815260208101839052610100870151906102f882611a56565b506104e36040516104c2816111e1565b6356b07d1360e11b8152602081018390526101008701519061032e82611a66565b506105146040516104f3816111e1565b631df68add60e21b8152602081018390526101008701519061036482611a76565b5060405190610522826111e1565b63278f794360e11b825260208201526101008501519061039682611a86565b50610578604051610551816111e1565b630b135d3f60e11b8152602081018390526101008501519061057282611a96565b52611a96565b506105af604051610588816111e1565b6317aa5fb760e11b815260208101839052610100850151906105a982611aa6565b52611aa6565b506105e66040516105bf816111e1565b63affe39c160e01b815260208101839052610100850151906105e082611ab7565b52611ab7565b50604051906105f4826111e1565b6342580cb760e11b825260208201526101008301519061061382611ac8565b52611ac8565b5060405180916020825261068661067061065a61064484516101a060208801526101c0870190611419565b6020850151868203601f19016040880152611419565b6040840151858203601f19016060870152611419565b6060830151848203601f19016080860152611419565b6080820151151560a084015260a0820151151560c084015260c082015190601f198482030160e0850152815180825260208201916020808360051b8301019401926000915b83831061077d578780610779896107466106f68b60e0840151601f198783030161010088015261149c565b6107306107176101008501519261012093601f1989830301858a015261149c565b918401519161014092601f19888303018489015261149c565b90830151858203601f190161016087015261149c565b6107646101608301519161018092601f1987830301848801526114f0565b910151838203601f19016101a08501526114f0565b0390f35b919395965091936020806107c1600193601f19868203018752606060408b51878060a01b038151168452858101511515868501520151918160408201520190611419565b97019301930190928796959492936106cb565b6020906040516107e3816111e1565b600081526040516107f3816111fc565b60008152600084820152600060408201528382015282828801015201610407565b634e487b7160e01b600052604160045260246000fd5b602090604051610839816111e1565b60008152604051610849816111fc565b6000815260008482015260006040820152838201528282850101520161024a565b346101545760ff61087a3661110f565b5050509116156000146108c6576001600160a01b03811633141590816108b4575b506108a257005b60405163ea8e4eb560e01b8152600490fd5b6108bf9150336119f7565b158161089b565b60405163d623472560e01b8152600490fd5b34610154576000366003190112610154576107796108fd6108f833611aed565b611c16565b604051918291602083526020830190611164565b3461015457602061092a610924366113d5565b91611c7b565b604051908152f35b34610154576020366003190112610154576004356001600160401b038111610154576109629036906004016110e2565b505061096d3361159a565b60608101906001805b15610995575b61fffe1916825260808120805460009182905590610976565b6001811615806109b2575b61097c57600183526080822060009055005b508015156109a0565b34610154576109c9366113d5565b505060405163d623472560e01b815260049150fd5b346101545760206109f76109f1366112c3565b906118de565b818151910120604051908152f35b3461015457600036600319011261015457610a3f7f00000000000000000000000000000000000000000000000000000000000000006115c1565b610a687f00000000000000000000000000000000000000000000000000000000000000006116c6565b6040516020808201928284106001600160401b03851117610814579181610ac18594610ab3979660405260008452604051978897600f60f81b895260e0858a015260e08901906111a1565b9087820360408901526111a1565b91466060870152336080870152600060a087015285830360c0870152519182815201929160005b828110610af757505050500390f35b835185528695509381019392810192600101610ae8565b611387565b34610154576020366003190112610154576001600160401b0360043581811161015457610b449036906004016110e2565b919091333b15610bb257610b5733611555565b15610ba15782019060208383031261015457823590811161015457610b7c9201611319565b805115610b8f57610b8d9033611dd5565b005b604051639aa6ffc360e01b8152600490fd5b60405162dc149f60e41b8152600490fd5b604051635d3944c960e11b8152600490fd5b3461015457610bd23661110f565b5050505050600460405163d623472560e01b8152fd5b34610154576040366003190112610154576001600160401b0360043581811161015457610c19903690600401611319565b602491823590811161015457610c33903690600401611319565b91610c3d33611555565b610d3257610c4b8233611dd5565b825160005b818110610cd8575050506080610c653361159a565b6001606082015220548015908115610ccd575b50610b8f57610cb97f8102106be6867f3566db7ac13c2a7afdcb3cba87576742016de13819f97e04e691610cc8604051928392604084526040840190611164565b82810360208401523395611164565b0390a2005b600191501683610c78565b610cf96001600160601b0319610cee8388611ad9565b5160601b1633611ed3565b15610d0657600101610c50565b82906001600160a01b0390610d1b9087611ad9565b5160405163298f10e760e11b815291166004820152fd5b6040516321c4e35760e21b8152600490fd5b3461015457602036600319011261015457602061014a610d626110cc565b336119f7565b3461015457610779610d7c6109f1366112c3565b6040519182916020835260208301906111a1565b34610154576040366003190112610154576024356001600160401b03811161015457610dcd610dc560209236906004016112a5565b60043561177b565b6040516001600160e01b03199091168152f35b3461015457600036600319011261015457610df9611da1565b50610e02611da1565b60405190610e0f826111e1565b601282526020917126bab63a349027bbb732b91028363ab3b4b760711b838201528152604051610e3e816111e1565b60058152640312e302e360dc1b8382015282820190815260405190610e62826111e1565b6007825266416c6368656d7960c81b848301526040830191825260405191610e89836111e1565b601083526f04d6f64696679204f776e6572736869760841b8584015260405190610eb2826111e1565b60019384835260005b878110610fbb575090610ef9610f3f926060880194855260405190610edf826111e1565b633956224760e01b82528982015284519061028c82611a23565b50610f2e610f18604051978989525160808a8a015260a08901906111a1565b945194601f1995868983030160408a01526111a1565b9051848783030160608801526111a1565b9051948285830301608086015285519182815281810182808560051b8401019801946000925b858410610f7257888a0389f35b909192939495968580610fa88c8686869f030188526040838d5163ffffffff60e01b8151168452015191818582015201906111a1565b9b99019796959190910193019190610f65565b968093949597604051610fcd816111e1565b60008152606083820152828288010152019694939296610ebb565b346101545760403660031901126101545760043560ff811603610154576024356001600160401b038111610154576110249036906004016110e2565b505060405163d623472560e01b8152600490fd5b34610154576020366003190112610154576107796108fd6108f861105a6110cc565b611aed565b34610154576020366003190112610154576004359063ffffffff60e01b8216809203610154576020916316cc458360e21b81149081156110a1575b5015158152f35b63ea15602d60e01b8114915081156110bb575b508361109a565b6301ffc9a760e01b149050836110b4565b600435906001600160a01b038216820361015457565b9181601f84011215610154578235916001600160401b038311610154576020838186019501011161015457565b9060806003198301126101545760043560ff8116810361015457916024356001600160a01b0381168103610154579160443591606435906001600160401b03821161015457611160916004016110e2565b9091565b90815180825260208080930193019160005b828110611184575050505090565b83516001600160a01b031685529381019392810192600101611176565b919082519283825260005b8481106111cd575050826000602080949584010152601f8019910116010190565b6020818301810151848301820152016111ac565b604081019081106001600160401b0382111761081457604052565b606081019081106001600160401b0382111761081457604052565b60c081019081106001600160401b0382111761081457604052565b90601f801991011681019081106001600160401b0382111761081457604052565b6001600160401b03811161081457601f01601f191660200190565b92919261127a82611253565b916112886040519384611232565b829481845281830111610154578281602093846000960137010152565b9080601f83011215610154578160206112c09335910161126e565b90565b906040600319830112610154576004356001600160a01b03811681036101545791602435906001600160401b038211610154576112c0916004016112a5565b6001600160401b0381116108145760051b60200190565b9080601f8301121561015457602090823561133381611302565b936113416040519586611232565b818552838086019260051b820101928311610154578301905b828210611368575050505090565b81356001600160a01b038116810361015457815290830190830161135a565b346101545760a0366003190112610154576004356001600160a01b03811603610154576060366023190112610154576084356001600160401b03811161015457610b8d9036906004016110e2565b906003196060818401126101545760043560ff811681036101545792602435916001600160401b038311610154578261016092030112610154576004019060443590565b90815180825260208080930193019160005b828110611439575050505090565b83516001600160e01b0319168552938101939281019260010161142b565b6005111561146157565b634e487b7160e01b600052602160045260246000fd5b60408091805161148681611457565b845260ff60208201511660208501520151910152565b90815180825260208080930193019160005b8281106114bc575050505090565b909192938260806001926114e483895163ffffffff60e01b8151168452015184830190611477565b019501939291016114ae565b90815180825260208080930193019160005b828110611510575050505090565b909192938260e06001926115496040895163ffffffff851b815116845261153d8682015187860190611477565b01516080830190611477565b01950193929101611502565b604080516001600160a01b039092168252639cc6c92360e01b60208301526002908201526001606082015260809020548015908115611592575090565b600191501690565b604080516001600160a01b039092168252639cc6c92360e01b602083015260029082015290565b60ff81146115ff5760ff811690601f82116115ed57604051916115e3836111e1565b8252602082015290565b604051632cd44ac360e21b8152600490fd5b506040516000805490600182811c908084169384156116bc575b60209485841081146116a85783875286949392918115611688575060011461164a575b50506112c092500382611232565b600080805285812095935091905b8183106116705750506112c09350820101388061163c565b85548784018501529485019486945091830191611658565b9150506112c094925060ff191682840152151560051b820101388061163c565b634e487b7160e01b85526022600452602485fd5b91607f1691611619565b60ff81146116e85760ff811690601f82116115ed57604051916115e3836111e1565b50604051600060019081549182811c90808416938415611771575b60209485841081146116a8578387528694939291811561168857506001146117335750506112c092500382611232565b600081815285812095935091905b8183106117595750506112c09350820101388061163c565b85548784018501529485019486945091830191611741565b91607f1691611703565b61179b9060405190602082015260208152611795816111e1565b336118de565b60208151910120906117ad8183611825565b6117b981949294611457565b6117ff5750506080906117cb3361159a565b606091821b6001600160601b031916918101919091522054156117f357630b135d3f60e11b90565b6001600160e01b031990565b61180a925033611fb0565b61181a576001600160e01b031990565b630b135d3f60e11b90565b90604181511460001461184f57611160916020820151906060604084015193015160001a90611859565b5050600090600290565b9291906fa2a8918ca85bafe22016d0b997e4df60600160ff1b0383116118d25791608094939160ff602094604051948552168484015260408301526060820152600093849182805260015afa156118c55781516001600160a01b038116156118bf579190565b50600190565b50604051903d90823e3d90fd5b50505050600090600390565b9060208151910120604090815160208101917fa856bbdae1f2c6e4aa17a75ad7cc5650f184ec4b549174dd7258c9701d663fc6835283820152828152611923816111fc565b51902091815160208101917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f83527f0000000000000000000000000000000000000000000000000000000000000000848301527f0000000000000000000000000000000000000000000000000000000000000000606083015246608083015260018060a01b031660a082015260a081526119bc81611217565b5190209181519261190160f01b6020850152602284015260428301526042825260808201908282106001600160401b03831117610814575290565b90611a0360809261159a565b906bffffffffffffffffffffffff199060601b1660608201522054151590565b805115611a305760200190565b634e487b7160e01b600052603260045260246000fd5b805160011015611a305760400190565b805160021015611a305760600190565b805160031015611a305760800190565b805160041015611a305760a00190565b805160051015611a305760c00190565b805160061015611a305760e00190565b805160071015611a30576101000190565b805160081015611a30576101200190565b805160091015611a30576101400190565b8051821015611a305760209160051b010190565b60018060a01b031690604051828152639cc6c92360e01b92836020830152600291826040820152606081019160019383858095526080938585822054926000975b611bc5575b50505050611b4084611302565b96611b4e6040519889611232565b848852601f19611b5d86611302565b013660208a0137878515611bbb5750604051928352602083015260408201528360009060608301905b858310611b965750505050505050565b8152858484205461ffff198116611bad858c611ad9565b5292019161fffe1916611b86565b9750505050505050565b909192968288161580611c0d575b15611c045782019661fffe19811690851615611bfb57815285832054915b9081939293611b2e565b50600091611bf1565b96829150611b33565b50871515611bd3565b805160005b818110611c2757505090565b80611c3460019285611ad9565b5160601c611c428286611ad9565b5201611c1b565b903590601e198136030182121561015457018035906001600160401b0382116101545760200191813603831361015457565b91907f19457468657265756d205369676e6564204d6573736167653a0a33320000000060005281601c52603c6000209060ff6001611cd2610140840194611ccc611cc58787611c49565b369161126e565b90611825565b9290961614611ced5760405163d623472560e01b8152600490fd5b611cf681611457565b611d1557505050611d0790336119f7565b611d1057600190565b600090565b611d079350611d2791611cc591611c49565b9033611fb0565b604051906101a082018281106001600160401b038211176108145760405281610180606091828152826020820152826040820152828082015260006080820152600060a08201528260c08201528260e0820152826101008201528261012082015282610140820152826101608201520152565b60405190608082018281106001600160401b0382111761081457604052606080838181528160208201528160408201520152565b9080519160005b838110611de95750505050565b611e0a6001600160601b0319611dff8386611ad9565b5160601b1683611e44565b15611e1757600101611ddc565b6024906001600160a01b0390611e2d9085611ad9565b5160405163b20f76e360e01b815291166004820152fd5b9061ffff1916908115611ecc57604080516001600160a01b039092168252639cc6c92360e01b602083015260029082015260608101828152608082208054909290611ec357600160809252209182549283158015611eb9575b15611ead57600193505555600190565b90600217905555600190565b5060018416611e9d565b50505050600090565b5050600090565b919061ffff1916916040519060018060a01b03168152639cc6c92360e01b6020820152600280604083015260608201918483526080918282209283549287158015611fa8575b611f9c5760016000815b15611f6d575b508652818120805460009161fffe1980831691908c8314611f4c57505091611f23565b9550979850509480949198995061fffc925016931691161717905555600190565b60018116159081611f92575b5015611f855738611f29565b5060009750505050505050565b9050151538611f79565b50600096505050505050565b508315611f19565b6108f8611fbf91939293611aed565b9160005b8351811015611ec3576001600160a01b03611fde8286611ad9565b5116604090815190600080602092838501630b135d3f60e11b95868252896024820152876044820152612027816120198d60648301906111a1565b03601f198101835282611232565b51915afa923d1561209e573d61204861203f82611253565b92519283611232565b81523d60008383013e5b83612091575b83612076575b50505061206d57600101611fc3565b50505050600190565b9080929350818051810103126101545701511438808061205e565b9250808351101592612058565b50606061205256fea264697066735822122014abfc7a6de52a87457656e91b424fb7346335ace02ab4b337987018998b66b464736f6c63430008150033", + "nonce": "0x48", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x0f13a3b7e8528a2fb6f1b4ceb43330feab35d56a3ffc7ffc0cbe8bd16ea4d7ba", + "transactionType": "CREATE", + "contractName": "TokenReceiverPlugin", + "contractAddress": "0xa81C0AEaB22b21b4da8d8728063f6570384b48C9", + "function": null, + "arguments": null, + "transaction": { + "type": "0x02", + "from": "0x7f89ed1f3f0d52d303904101305471bca3cde710", + "gas": "0xfb789", + "value": "0x0", + "data": "0x6080806040523461001657610d5b908161001c8239f35b600080fdfe608060408181526004918236101561001657600080fd5b600090813560e01c90816223de29146107f857816301ffc9a7146107a0575080630c7ecd84146104ea5780631128186d14610755578063118a5389146105fd578063150b7a02146105a857806346d60eb214610586578063676c3dc2146105815780636d61fe701461057c5780637f4243381461058157806388e18ce4146105775780638a91b0e31461057c578063af87348314610577578063bc197c81146104ef578063bfd151c1146104ea578063c7763130146101355763f23a6e61146100de57600080fd5b346101325760a0366003190112610132576100f7610870565b5061010061088b565b50608435906001600160401b0382116101325750602092610123913691016108a1565b50505163f23a6e6160e01b8152f35b80fd5b5090346104e657816003193601126104e65761014f610c2b565b50610158610c2b565b90610161610c0c565b8481526101888360806020813682870137828601858152946223de2960e01b948591610c9e565b52630a85bd0160e11b918261019d8751610cc1565b5263f23a6e6160e01b94856101b28851610cd1565b5263bc197c8160e01b806101c68951610ce1565b526101cf610bb8565b600381528b858201528b8a8201526101e5610c0c565b978d89528c5b8581106104a05750906102736102959392610100809a019a8b5261022a610210610bed565b878152848a8201528c519061022482610c9e565b52610c9e565b50610250610236610bed565b8a8152848a8201528c519061024a82610cc1565b52610cc1565b50610259610bed565b90815282888201528a519061026d82610cd1565b52610cd1565b5061027c610bed565b9182528582015287519061028f82610ce1565b52610ce1565b508751938285018581106001600160401b0382111761048d5789939c50906103459b9492918497969752600387526102d7606097883686830137808d52610c9e565b526102e28a51610cc1565b52630271189760e51b6102f58a51610cd1565b5261035782519a8b98838a526103358c80519b866103206101a09e8f838501526101c0840190610a85565b92015190601f199e8f82850301910152610a85565b90518d82038c01898f0152610a85565b8a8701518c82038b018d8f0152610a85565b9a890151151560a08b015260a0890151151560c08b015260c089015194888b8d030160e08c01528b8651938482528380808401938760051b0101980195925b85841061043c578d806104388f8f8f8f8f8f6103f96103e26103cd61042794610410948c60e08c0151918b82850301910152610aff565b94519461012095888c830301878d0152610aff565b938701519361014094878b830301868c0152610aff565b928601519261016093868a830301858b0152610aff565b9185015191610180928589830301848a0152610b53565b930151918584030190850152610b53565b0390f35b90919293949597858f61047c838f878e8892879660019903018b5251878060a01b0381511684528581015115158685015201519181898201520190610a85565b9a0194019401929594939190610396565b634e487b7160e01b8c5260418d5260248cfd5b869798508d8c6104b598939495969798610bed565b918083526104c1610bb8565b9181835281868401528201528382015282828c01015201908b979695949392916101eb565b5080fd5b610923565b50346101325760a036600319011261013257610509610870565b5061051261088b565b506001600160401b03906044358281116104e6576105339036908601610a55565b50506064358281116104e65761054c9036908601610a55565b50506084359182116101325750602092610568913691016108a1565b50505163bc197c8160e01b8152f35b610a07565b6109d7565b610987565b50346101325750610596366108ce565b50505050505163d623472560e01b8152fd5b5034610132576080366003190112610132576105c2610870565b506105cb61088b565b50606435906001600160401b03821161013257506020926105ee913691016108a1565b505051630a85bd0160e11b8152f35b5034610132578060031936011261013257610616610cf1565b5061061f610cf1565b91610628610bed565b9160158352602092742a37b5b2b7102932b1b2b4bb32b91028363ab3b4b760591b848201528452610657610bed565b9160058352640312e302e360dc1b84840152838501928352610677610bed565b936007855266416c6368656d7960c81b8186015281860194855260606106d98351968388526106c96106b48a516080878c015260a08b0190610947565b975197601f1998898b830301888c0152610947565b90518789830301848a0152610947565b96015193808688030160808701528451928388528288019183808660051b8b0101970195985b858a1061070c5788880389f35b9091929394959685806107428386866001960301895287838d5163ffffffff60e01b815116845201519181858201520190610947565b99019501990198929190959493956106ff565b5034610132578160031936011261013257823560ff81160361013257602435906001600160401b038211610132575061079190369084016108a1565b50505163d623472560e01b8152fd5b905083346107f45760203660031901126107f4573563ffffffff60e01b81168091036107f4576020925063ea15602d60e01b81149081156107e3575b5015158152f35b6301ffc9a760e01b149050836107dc565b8280fd5b8285346104e65760c03660031901126104e657610813610870565b5061081c61088b565b506044356001600160a01b038116036104e6576001600160401b039060843582811161086c5761084f90369083016108a1565b505060a4359182116107f457610867913691016108a1565b505080f35b8380fd5b600435906001600160a01b038216820361088657565b600080fd5b602435906001600160a01b038216820361088657565b9181601f84011215610886578235916001600160401b038311610886576020838186019501011161088657565b9060806003198301126108865760043560ff8116810361088657916024356001600160a01b0381168103610886579160443591606435906001600160401b0382116108865761091f916004016108a1565b9091565b3461088657610931366108ce565b5050505050600460405163d623472560e01b8152fd5b919082519283825260005b848110610973575050826000602080949584010152601f8019910116010190565b602081830181015184830182015201610952565b346108865760a0366003190112610886576004356001600160a01b03811603610886576060366023190112610886576084356001600160401b038111610886576109d59036906004016108a1565b005b34610886576020366003190112610886576004356001600160401b038111610886576109d59036906004016108a1565b34610886576003196060368201126108865760043560ff81160361088657602435906001600160401b0382116108865761016091360301126108865760405163d623472560e01b8152600490fd5b9181601f84011215610886578235916001600160401b038311610886576020808501948460051b01011161088657565b90815180825260208080930193019160005b828110610aa5575050505090565b83516001600160e01b03191685529381019392810192600101610a97565b80516005811015610ae9576040918291845260ff60208201511660208501520151910152565b634e487b7160e01b600052602160045260246000fd5b90815180825260208080930193019160005b828110610b1f575050505090565b90919293826080600192610b4783895163ffffffff60e01b8151168452015184830190610ac3565b01950193929101610b11565b90815180825260208080930193019160005b828110610b73575050505090565b909192938260e0600192610bac6040895163ffffffff851b8151168452610ba08682015187860190610ac3565b01516080830190610ac3565b01950193929101610b65565b60405190606082018281106001600160401b03821117610bd757604052565b634e487b7160e01b600052604160045260246000fd5b60405190604082018281106001600160401b03821117610bd757604052565b6040519060a082018281106001600160401b03821117610bd757604052565b604051906101a082018281106001600160401b03821117610bd75760405281610180606091828152826020820152826040820152828082015260006080820152600060a08201528260c08201528260e0820152826101008201528261012082015282610140820152826101608201520152565b805115610cab5760200190565b634e487b7160e01b600052603260045260246000fd5b805160011015610cab5760400190565b805160021015610cab5760600190565b805160031015610cab5760800190565b60405190608082018281106001600160401b03821117610bd75760405260608083818152816020820152816040820152015256fea2646970667358221220280e8925b6d1a8dd02f2ebd2820b719889039bd6946c72d5e49ae8e6fb24505364736f6c63430008150033", + "nonce": "0x49", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xcefba29b52d19365a4b859228052e7415eed0576786ea4330dbe977cf2dbf7cc", + "transactionType": "CREATE", + "contractName": "MultiOwnerMSCAFactory", + "contractAddress": "0xFD14c78640d72f73CC88238E2f7Df3273Ee84043", + "function": null, + "arguments": [ + "0x7f89Ed1F3F0d52d303904101305471bca3cde710", + "0x56bC629F342821FBe91C5273880792dFECBE7920", + "0xb2b748c2557c552B8636862E41aB3649319dD045", + "0x220f6919637b5bebbfca862c1c942381e30468578a1a80f174db1664282c1ab2", + "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" + ], + "transaction": { + "type": "0x02", + "from": "0x7f89ed1f3f0d52d303904101305471bca3cde710", + "gas": "0x1642a8", + "value": "0x0", + "data": "0x610100346100f657601f61144138819003918201601f19168301916001600160401b038311848410176100fb5780849260a0946040528339810103126100f65761004881610111565b9061005560208201610111565b61006160408301610111565b90608060608401519301519360018060a01b03851685036100f65761008e9061008933610125565b610125565b60805260a05260c05260e0526040516112d4908161016d823960805181818161070301526108ec015260a05181818161030a015281816107f60152610b88015260c05181610934015260e05181818160e40152818161024c015281816105df015261065d0152f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b03821682036100f657565b600080546001600160a01b039283166001600160a01b03198216811783559216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a356fe60808060405260043610156200001f575b5036156200001d57600080fd5b005b600090813560e01c90816356973ee51462000b7257508063715018a61462000b1457806373876732146200073257806381785dfd14620006eb5780638da5cb5b14620006c2578063bb9fe6bf146200063d578063c23a5cea14620005ac578063d9caed1214620003a9578063e189e379146200027b578063e8eb3cc61462000234578063f2fde38b146200016a5763fbb1c3d4036200001057604036600319011262000155578060043563ffffffff81168091036200016757620000e262000c27565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690813b1562000163578290602460405180948193621cb65b60e51b835260048301528235905af180156200015857620001425750f35b6200014d9062000c80565b620001555780f35b80fd5b6040513d84823e3d90fd5b5050fd5b50fd5b50346200015557602036600319011262000155576200018862000c10565b6200019262000c27565b6001600160a01b03908116908115620001e0576000548260018060a01b0319821617600055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a380f35b60405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608490fd5b503462000155578060031936011262000155576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50346200015557620002cb62000397620002af620002993662000bbb565b6040516020969394909283929088840162000cd5565b0392620002c5601f199485810184528362000cb2565b62000e5a565b90620003806200038d856104169360405190620002eb8387018362000cb2565b8582528282019562000e898739604051620003568482019282620003497f00000000000000000000000000000000000000000000000000000000000000008660609160018060a01b0316815260406020820152600060408201520190565b0390810183528262000cb2565b60405195869362000370868601998a925192839162000d4b565b8401915180938684019062000d4b565b0103808452018262000cb2565b5190209062000d97565b6040516001600160a01b039091168152f35b5034620001555760603660031901126200015557620003c762000c10565b6001600160a01b03906024358281169190829003620005a7578391620003ec62000c27565b8062000427575081809381924791839183156200041c575b1690f115620004105780f35b604051903d90823e3d90fd5b6108fc925062000404565b91506040516020938482019263a9059cbb60e01b8452166024820152604435604482015260448152608081019167ffffffffffffffff9180841083851117620005825786918291856040526200047d8662000c95565b8786527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460a0820152519082875af13d1562000598573d9182116200058257620004e89360405192620004da87601f19601f840116018562000cb2565b83523d878785013e62000db7565b80518281159182156200055b575b505090501562000504575080f35b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152fd5b83809293500103126200057e5781015180151581036200057e57808238620004f6565b8280fd5b634e487b7160e01b600052604160045260246000fd5b620004e8936060925062000db7565b600080fd5b503462000155576020366003190112620001555780620005cb62000c10565b620005d562000c27565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169190823b1562000638576024849283604051958694859363611d2e7560e11b85521660048401525af180156200015857620001425750f35b505050fd5b503462000155578060031936011262000155576200065a62000c27565b807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b156200016757819060046040518094819363bb9fe6bf60e01b83525af180156200015857620006b4575080f35b620006bf9062000c80565b80f35b50346200015557806003193601126200015557546040516001600160a01b039091168152602090f35b503462000155578060031936011262000155576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50346200015557620007443662000bbb565b90929160405191620007568362000c95565b60018352835b6020811062000b02575062000794620007bf939495620007856040519384926020840162000cd5565b03601f19810183528262000cb2565b6200079f8462000d27565b52620007ab8362000d27565b50620007b78362000d27565b519062000e5a565b6040519190610416620007d6602082018562000cb2565b80845262000e8993620008516020820183878239620003806200084760207f000000000000000000000000000000000000000000000000000000000000000095604051828101906200035681620007858b8560609160018060a01b0316815260406020820152600060408201520190565b5190208462000d97565b94853b156200086f575b6040516001600160a01b0387168152602090f35b604051928084019184831067ffffffffffffffff84111762000aee5791849391620008b89385396001600160a01b03909116815260406020820181905260009082015260600190565b039085f515620001585760405190620008d18262000c95565b6001825260208201906020368337620008ea8362000d27565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169052604051620009248162000c95565b60018152602081019060203683377f00000000000000000000000000000000000000000000000000000000000000006200095e8262000d27565b5260405192839160608301906040602085015251809152608083019390895b81811062000ad457505050601f19828403016040830152805180845260208401936020808360051b8301019301948a915b83831062000aa05750505050620009cf925003601f19810183528262000cb2565b6001600160a01b0384163b1562000a9c57918491604051938492631cd3c49560e31b845260448401906040600486015251809152606484019290855b81811062000a7657505050828203600319016024840152829162000a2f9162000d70565b0381836001600160a01b0387165af1801562000a6b576020935062000a59575b808080806200085b565b62000a649062000c80565b3862000a4f565b6040513d85823e3d90fd5b82516001600160a01b031685528996508795506020948501949092019160010162000a0b565b8480fd5b9193600191939550602062000ac18192601f19868203018752895162000d70565b97019301930190928694929593620009ae565b82518652602095860195879550909201916001016200097d565b634e487b7160e01b89526041600452602489fd5b8060606020809387010152016200075c565b5034620001555780600319360112620001555762000b3162000c27565b80546001600160a01b03198116825581906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b90503462000bb7578160031936011262000bb7577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5080fd5b6040600319820112620005a7576004359160243567ffffffffffffffff92838211620005a75780602383011215620005a7578160040135938411620005a75760248460051b83010111620005a7576024019190565b600435906001600160a01b0382168203620005a757565b6000546001600160a01b0316330362000c3c57565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b67ffffffffffffffff81116200058257604052565b6040810190811067ffffffffffffffff8211176200058257604052565b90601f8019910116810190811067ffffffffffffffff8211176200058257604052565b9091604060209282848201858352520192916000805b83821062000cfb57505050505090565b9091929394853560018060a01b0381168091036200057e57815283019483019392916001019062000ceb565b80511562000d355760200190565b634e487b7160e01b600052603260045260246000fd5b60005b83811062000d5f5750506000910152565b818101518382015260200162000d4e565b9060209162000d8b8151809281855285808601910162000d4b565b601f01601f1916010190565b605591600b9160405191604083015260208201523081520160ff81532090565b9192901562000e1c575081511562000dcd575090565b3b1562000dd75790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b82519091501562000e305750805190602001fd5b60405162461bcd60e51b81526020600482015290819062000e5690602483019062000d70565b0390fd5b9062000e82620007859160405192839160208301958652604080840152606083019062000d70565b5190209056fe604060808152610416908138038061001681610218565b93843982019181818403126102135780516001600160a01b038116808203610213576020838101516001600160401b0394919391858211610213570186601f820112156102135780519061007161006c83610253565b610218565b918083528583019886828401011161021357888661008f930161026e565b813b156101b9577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b031916841790556000927fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b8480a28051158015906101b2575b61010b575b855160d190816103458239f35b855194606086019081118682101761019e578697849283926101889952602788527f416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c87890152660819985a5b195960ca1b8a8901525190845af4913d15610194573d9061017a61006c83610253565b91825281943d92013e610291565b508038808080806100fe565b5060609250610291565b634e487b7160e01b84526041600452602484fd5b50826100f9565b855162461bcd60e51b815260048101859052602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608490fd5b600080fd5b6040519190601f01601f191682016001600160401b0381118382101761023d57604052565b634e487b7160e01b600052604160045260246000fd5b6001600160401b03811161023d57601f01601f191660200190565b60005b8381106102815750506000910152565b8181015183820152602001610271565b919290156102f357508151156102a5575090565b3b156102ae5790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b8251909150156103065750805190602001fd5b6044604051809262461bcd60e51b825260206004830152610336815180928160248601526020868601910161026e565b601f01601f19168101030190fdfe608060405236156054577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc54600090819081906001600160a01b0316368280378136915af43d82803e156050573d90f35b3d90fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc54600090819081906001600160a01b0316368280378136915af43d82803e156050573d90f3fea2646970667358221220431fb6d6360be5bcc06b4ceb4b8305301f868185e8dcb3820a2de9d256a3321464736f6c63430008150033a2646970667358221220c509919fb7287783a5c402c342758273c381b377054a31beab5e33b6a5df67b264736f6c634300081500330000000000000000000000007f89ed1f3f0d52d303904101305471bca3cde71000000000000000000000000056bc629f342821fbe91c5273880792dfecbe7920000000000000000000000000b2b748c2557c552b8636862e41ab3649319dd045220f6919637b5bebbfca862c1c942381e30468578a1a80f174db1664282c1ab20000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d2789", + "nonce": "0x4a", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0xb2179964a1836d4510adb53395bbf338881895fb1943e077bfffdea583a5fa57", + "transactionType": "CALL", + "contractName": "MultiOwnerMSCAFactory", + "contractAddress": "0xFD14c78640d72f73CC88238E2f7Df3273Ee84043", + "function": "addStake(uint32,uint256)", + "arguments": [ + "86400", + "1" + ], + "transaction": { + "type": "0x02", + "from": "0x7f89ed1f3f0d52d303904101305471bca3cde710", + "to": "0xfd14c78640d72f73cc88238e2f7df3273ee84043", + "gas": "0x1d0da", + "value": "0x1", + "data": "0xfbb1c3d400000000000000000000000000000000000000000000000000000000000151800000000000000000000000000000000000000000000000000000000000000001", + "nonce": "0x4b", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x5cb7347b960e28bd3bf3a4958f438cd477ed9a2bdf2c2862ade7b33ce951b14c", + "transactionType": "CREATE", + "contractName": "MultiOwnerTokenReceiverMSCAFactory", + "contractAddress": "0x22322E35c1850F26DD54Ed8F59a27C1c79847A15", + "function": null, + "arguments": [ + "0x7f89Ed1F3F0d52d303904101305471bca3cde710", + "0x56bC629F342821FBe91C5273880792dFECBE7920", + "0xa81C0AEaB22b21b4da8d8728063f6570384b48C9", + "0xb2b748c2557c552B8636862E41aB3649319dD045", + "0x220f6919637b5bebbfca862c1c942381e30468578a1a80f174db1664282c1ab2", + "0x011886f4265b159d818bf87d48a5e63e27be65fb8150c6badf48b183d2deea9d", + "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" + ], + "transaction": { + "type": "0x02", + "from": "0x7f89ed1f3f0d52d303904101305471bca3cde710", + "gas": "0x177a88", + "value": "0x0", + "data": "0x610140346200013b57601f620015a038819003918201601f19168301916001600160401b03831184841017620001405780849260e0946040528339810103126200013b576200004e8162000156565b6200005c6020830162000156565b6200006a6040840162000156565b620000786060850162000156565b9060808501519260c060a08701519601519460018060a01b03861686036200013b57620000b090620000aa336200016b565b6200016b565b60805260a05260c05260e052610100918252610120908152604051906113ed9283620001b3843960805183818161078d0152610976015260a05183818161060701526109ad015260c051838181610316015281816108800152610c76015260e051836109f501525182610a2201525181818160f0015281816102580152818161066901526106e70152f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b03821682036200013b57565b600080546001600160a01b039283166001600160a01b03198216811783559216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a356fe60808060405260043610156200001f575b5036156200001d57600080fd5b005b600090813560e01c90816356973ee51462000c6057508063715018a61462000c025780637387673214620007bc57806381785dfd14620007755780638da5cb5b146200074c578063bb9fe6bf14620006c7578063c23a5cea1462000636578063c3ed7eb514620005ef578063d9caed1214620003b5578063e189e3791462000287578063e8eb3cc61462000240578063f2fde38b14620001765763fbb1c3d4036200001057604036600319011262000161578060043563ffffffff81168091036200017357620000ee62000d15565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690813b156200016f578290602460405180948193621cb65b60e51b835260048301528235905af1801562000164576200014e5750f35b620001599062000d6e565b620001615780f35b80fd5b6040513d84823e3d90fd5b5050fd5b50fd5b50346200016157602036600319011262000161576200019462000cfe565b6200019e62000d15565b6001600160a01b03908116908115620001ec576000548260018060a01b0319821617600055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a380f35b60405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608490fd5b503462000161578060031936011262000161576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50346200016157620002d7620003a3620002bb620002a53662000ca9565b6040516020969394909283929088840162000dd9565b0392620002d1601f199485810184528362000db6565b62000f73565b906200038c62000399856104169360405190620002f78387018362000db6565b8582528282019562000fa28739604051620003628482019282620003557f00000000000000000000000000000000000000000000000000000000000000008660609160018060a01b0316815260406020820152600060408201520190565b0390810183528262000db6565b6040519586936200037c868601998a925192839162000e64565b8401915180938684019062000e64565b0103808452018262000db6565b5190209062000eb0565b6040516001600160a01b039091168152f35b5034620001615760603660031901126200016157620003d362000cfe565b6001600160a01b039060248035838116929190839003620005ea578492620003fa62000d15565b806200043657505081809381924791839183156200042b575b1690f1156200041f5780f35b604051903d90823e3d90fd5b6108fc925062000413565b909250604051906020948583019363a9059cbb60e01b85521684830152604435604483015260448252608082019267ffffffffffffffff9280851084861117620005d55760c0810185811085821117620005c0576040528685527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460a082015251879182919082855af1903d15620005af573d9283116200059a57906200050093929160405192620004f288601f19601f840116018562000db6565b83523d888885013e62000ed0565b805183811591821562000573575b50509050156200051d57505080f35b90602a6084926040519262461bcd60e51b845260048401528201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152fd5b83809293500103126200059657820151801515810362000596578083386200050e565b8380fd5b84634e487b7160e01b60005260416004526000fd5b906200050093925060609162000ed0565b86634e487b7160e01b60005260416004526000fd5b85634e487b7160e01b60005260416004526000fd5b600080fd5b503462000161578060031936011262000161576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034620001615760203660031901126200016157806200065562000cfe565b6200065f62000d15565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169190823b15620006c2576024849283604051958694859363611d2e7560e11b85521660048401525af1801562000164576200014e5750f35b505050fd5b50346200016157806003193601126200016157620006e462000d15565b807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b156200017357819060046040518094819363bb9fe6bf60e01b83525af1801562000164576200073e575080f35b620007499062000d6e565b80f35b50346200016157806003193601126200016157546040516001600160a01b039091168152602090f35b503462000161578060031936011262000161576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50346200016157620007ce3662000ca9565b90929160405191620007e08362000d99565b60028352835b6040811062000bf057506200081e620008499394956200080f6040519384926020840162000dd9565b03601f19810183528262000db6565b620008298462000e2f565b52620008358362000e2f565b50620008418362000e2f565b519062000f73565b604051919061041662000860602082018562000db6565b80845262000fa293620008db60208201838782396200038c620008d160207f0000000000000000000000000000000000000000000000000000000000000000956040518281019062000362816200080f8b8560609160018060a01b0316815260406020820152600060408201520190565b5190208462000eb0565b94853b15620008f9575b6040516001600160a01b0387168152602090f35b604051928084019184831067ffffffffffffffff84111762000bdc5791849391620009429385396001600160a01b03909116815260406020820181905260009082015260600190565b039085f5156200016457604051906200095b8262000d99565b6002825260208201906040368337620009748362000e2f565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169052620009ab8362000e53565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169052604051620009e58162000d99565b60028152602081019060403683377f000000000000000000000000000000000000000000000000000000000000000062000a1f8262000e2f565b527f000000000000000000000000000000000000000000000000000000000000000062000a4c8262000e53565b5260405192839160608301906040602085015251809152608083019390895b81811062000bc257505050601f19828403016040830152805180845260208401936020808360051b8301019301948a915b83831062000b8e575050505062000abd925003601f19810183528262000db6565b6001600160a01b0384163b1562000b8a57918491604051938492631cd3c49560e31b845260448401906040600486015251809152606484019290855b81811062000b6457505050828203600319016024840152829162000b1d9162000e89565b0381836001600160a01b0387165af1801562000b59576020935062000b47575b80808080620008e5565b62000b529062000d6e565b3862000b3d565b6040513d85823e3d90fd5b82516001600160a01b031685528996508795506020948501949092019160010162000af9565b8480fd5b9193600191939550602062000baf8192601f19868203018752895162000e89565b9701930193019092869492959362000a9c565b825186526020958601958795509092019160010162000a6b565b634e487b7160e01b89526041600452602489fd5b806060602080938701015201620007e6565b5034620001615780600319360112620001615762000c1f62000d15565b80546001600160a01b03198116825581906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b90503462000ca5578160031936011262000ca5577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5080fd5b6040600319820112620005ea576004359160243567ffffffffffffffff92838211620005ea5780602383011215620005ea578160040135938411620005ea5760248460051b83010111620005ea576024019190565b600435906001600160a01b0382168203620005ea57565b6000546001600160a01b0316330362000d2a57565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b67ffffffffffffffff811162000d8357604052565b634e487b7160e01b600052604160045260246000fd5b6060810190811067ffffffffffffffff82111762000d8357604052565b90601f8019910116810190811067ffffffffffffffff82111762000d8357604052565b9091604060209282848201858352520192916000805b83821062000dff57505050505090565b9091929394853560018060a01b03811680910362000e2b57815283019483019392916001019062000def565b8280fd5b80511562000e3d5760200190565b634e487b7160e01b600052603260045260246000fd5b80516001101562000e3d5760400190565b60005b83811062000e785750506000910152565b818101518382015260200162000e67565b9060209162000ea48151809281855285808601910162000e64565b601f01601f1916010190565b605591600b9160405191604083015260208201523081520160ff81532090565b9192901562000f35575081511562000ee6575090565b3b1562000ef05790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b82519091501562000f495750805190602001fd5b60405162461bcd60e51b81526020600482015290819062000f6f90602483019062000e89565b0390fd5b9062000f9b6200080f9160405192839160208301958652604080840152606083019062000e89565b5190209056fe604060808152610416908138038061001681610218565b93843982019181818403126102135780516001600160a01b038116808203610213576020838101516001600160401b0394919391858211610213570186601f820112156102135780519061007161006c83610253565b610218565b918083528583019886828401011161021357888661008f930161026e565b813b156101b9577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b031916841790556000927fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b8480a28051158015906101b2575b61010b575b855160d190816103458239f35b855194606086019081118682101761019e578697849283926101889952602788527f416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c87890152660819985a5b195960ca1b8a8901525190845af4913d15610194573d9061017a61006c83610253565b91825281943d92013e610291565b508038808080806100fe565b5060609250610291565b634e487b7160e01b84526041600452602484fd5b50826100f9565b855162461bcd60e51b815260048101859052602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608490fd5b600080fd5b6040519190601f01601f191682016001600160401b0381118382101761023d57604052565b634e487b7160e01b600052604160045260246000fd5b6001600160401b03811161023d57601f01601f191660200190565b60005b8381106102815750506000910152565b8181015183820152602001610271565b919290156102f357508151156102a5575090565b3b156102ae5790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b8251909150156103065750805190602001fd5b6044604051809262461bcd60e51b825260206004830152610336815180928160248601526020868601910161026e565b601f01601f19168101030190fdfe608060405236156054577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc54600090819081906001600160a01b0316368280378136915af43d82803e156050573d90f35b3d90fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc54600090819081906001600160a01b0316368280378136915af43d82803e156050573d90f3fea2646970667358221220431fb6d6360be5bcc06b4ceb4b8305301f868185e8dcb3820a2de9d256a3321464736f6c63430008150033a264697066735822122037146aa65f55e811be3ec40b4985aa1c3023160c64d100dd47e2045f78d38bc464736f6c634300081500330000000000000000000000007f89ed1f3f0d52d303904101305471bca3cde71000000000000000000000000056bc629f342821fbe91c5273880792dfecbe7920000000000000000000000000a81c0aeab22b21b4da8d8728063f6570384b48c9000000000000000000000000b2b748c2557c552b8636862e41ab3649319dd045220f6919637b5bebbfca862c1c942381e30468578a1a80f174db1664282c1ab2011886f4265b159d818bf87d48a5e63e27be65fb8150c6badf48b183d2deea9d0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d2789", + "nonce": "0x4c", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x045e474abf86398ebbf1814e8efd5407a1800a52d6d3bc30be6e0e7d2536db54", + "transactionType": "CALL", + "contractName": "MultiOwnerTokenReceiverMSCAFactory", + "contractAddress": "0x22322E35c1850F26DD54Ed8F59a27C1c79847A15", + "function": "addStake(uint32,uint256)", + "arguments": [ + "86400", + "1" + ], + "transaction": { + "type": "0x02", + "from": "0x7f89ed1f3f0d52d303904101305471bca3cde710", + "to": "0x22322e35c1850f26dd54ed8f59a27c1c79847a15", + "gas": "0x1b724", + "value": "0x1", + "data": "0xfbb1c3d400000000000000000000000000000000000000000000000000000000000151800000000000000000000000000000000000000000000000000000000000000001", + "nonce": "0x4d", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x8d523325b3f21dedb6e264a1da4295ef0e00cc59c3d2c26d6dcea079f6b00e47", + "transactionType": "CREATE", + "contractName": "SessionKeyPlugin", + "contractAddress": "0x588dCE36DdeB0e40320AD1d9909a1f86053D7Df1", + "function": null, + "arguments": null, + "transaction": { + "type": "0x02", + "from": "0x7f89ed1f3f0d52d303904101305471bca3cde710", + "gas": "0x205f14", + "value": "0x0", + "data": "0x6080806040523461001657611c8a908161001c8239f35b600080fdfe608080604052600436101561001357600080fd5b60003560e01c90816301ffc9a714610f92575080630b45f4b414610f665780630c7ecd84146107cf5780631128186d14610f16578063118a538914610d0b5780632237570a14610cdd57806331d99c2c14610ab857806336d0b31a14610a9157806346d60eb214610a6d578063676c3dc2146108a15780636d61fe70146108d7578063717493c7146108a65780637f424338146108a157806388e18ce41461087e5780638a91b0e3146107f5578063af873483146107d4578063bfd151c1146107cf578063c77631301461029b578063ca9b97f31461014b5763fd5e71c5146100fb57600080fd5b346101465760203660031901126101465760206080610118610fff565b6101213361169f565b906bffffffffffffffffffffffff199060601b16606082015220541515604051908152f35b600080fd5b34610146576040366003190112610146576001600160401b036004358181116101465761017c903690600401611109565b602492833590808211610146573660238301121561014657816004013590811161014657848201918536918360061b0101116101465760005b81811061022d5750505060005b8181106101cb57005b6101f46001600160601b03196101ea6101e5848688611709565b6113bb565b60601b1633611719565b15610201576001016101c2565b6101e59161020e93611709565b60405163512c6ac960e11b81526001600160a01b039091166004820152fd5b6102616001600160601b03196102476101e58486886116f9565b60601b1660206102588486886116f9565b013590336117a8565b1561026e576001016101b5565b6101e59061027d9287946116f9565b604051629c6fbb60e21b81526001600160a01b039091166004820152fd5b34610146576000366003190112610146576102b4611bad565b506102bd611bad565b6102ef6040516102cc81611368565b6002815260403660208301376020830181905263ea15602d60e01b91829161141c565b526102fd6020830151611429565b5260405160a081018181106001600160401b038211176107635760405260048152608036602083013780604083015261033d630c76670b60e21b9161141c565b5263717493c760e01b6103536040830151611429565b5263fd5e71c560e01b6103696040830151611439565b5260408101518051600310156107b957608063ca9b97f360e01b9101526040519061039382611368565b6001825260006020830152600060408301526040516103b181611368565b600281526000602082015260006040820152604051926103d084611368565b6002845260005b60408110610779575061041f90839460e0610451950152604051906103fb8261134d565b630c76670b60e21b8252602082015260e0850151906104198261141c565b5261141c565b506040519061042d8261134d565b63ca9b97f360e01b8252602082015260e08301519061044b82611429565b52611429565b506040519061045f82611368565b60038252600060208301526000604083015260405161047d81611368565b60028152600060208201526001604082015260405192608084018481106001600160401b03821117610763576040526003845260005b60608110610723575061052890839461010061055b9501526104fb6040516104da8161134d565b63717493c760e01b815260208101839052610100870151906104198261141c565b50604051906105098261134d565b63fd5e71c560e01b825260208201526101008501519061044b82611429565b50604051906105368261134d565b63ca9b97f360e01b825260208201526101008301519061055582611439565b52611439565b5060808101906001825260a081019060018252604051928392602084526105db6105c56105af61059986516101a060208a01526101c0890190611211565b6020870151888203601f190160408a0152611211565b6040860151878203601f19016060890152611211565b6060850151868203601f19016080880152611211565b9151151560a085015251151560c084015260c082015190601f198482030160e0850152815180825260208201916020808360051b8301019401926000915b8383106106cc5787806106c8896106418a60e0830151601f1986830301610100870152611294565b6106b361069a6106816106686101008601519461012095601f198a830301878b0152611294565b938501519361014094601f1989830301868a0152611294565b928401519261016093601f198883030185890152611294565b918301519161018092601f1987830301848801526112e8565b910151838203601f19016101a08501526112e8565b0390f35b91939596509193602080610710600193601f19868203018752606060408b51878060a01b038151168452858101511515868501520151918160408201520190611211565b9701930193019092879695949293610619565b6020906040516107328161134d565b6000815260405161074281611368565b600081526000848201526000604082015283820152828288010152016104b3565b634e487b7160e01b600052604160045260246000fd5b6020906040516107888161134d565b6000815260405161079881611368565b600081526000848201526000604082015283820152828288010152016103d7565b634e487b7160e01b600052603260045260246000fd5b610a6d565b346101465760206107ed6107e7366111cd565b91611902565b604051908152f35b34610146576020366003190112610146576004356001600160401b0381116101465761082590369060040161103f565b50506108303361169f565b60608101906001805b15610858575b61fffe1916825260808120805460009182905590610839565b600181161580610875575b61083f57600183526080822060009055005b50801515610863565b346101465761088c366111cd565b505060405163d623472560e01b815260049150fd5b61117d565b34610146576000366003190112610146576106c86108cb6108c63361145d565b6116c6565b60405191829182611139565b3461014657602080600319360112610146576001600160401b03906004358281116101465761090a90369060040161103f565b929091333b15610a5b5761091d3361169f565b91600194608060609487868201522054868115918215610a51575b505015610a4057840193828186031261014657803591821161014657019280601f8501121561014657833561096c816113a4565b9461097a6040519687611383565b818652838087019260051b820101928311610146578301905b828210610a29575050505081519160005b8381106109ad57005b6109b78183611449565b516001600160a01b03908181168015610a1157506109e190851b6001600160601b03191633611719565b156109ee575084016109a4565b6109fa60249284611449565b5160405163512c6ac960e11b815291166004820152fd5b6024906040519063d3d0f65960e01b82526004820152fd5b838091610a358461102b565b815201910190610993565b60405162dc149f60e41b8152600490fd5b1690508688610938565b604051635d3944c960e11b8152600490fd5b3461014657610a7b3661106c565b5050505050600460405163d623472560e01b8152fd5b34610146576020366003190112610146576106c86108cb6108c6610ab3610fff565b61145d565b34610146576040366003190112610146576001600160401b0360043581811161014657610ae9903690600401611109565b610af1611015565b50610afb816113a4565b91610b096040519384611383565b818352610b15826113a4565b60209490601f199081018660005b828110610cce5750505060005b848110610b9557604080518881528751818a01819052600092600582901b83018101918a8c01918c9085015b828710610b695785850386f35b909192938280610b85600193603f198a820301865288516110e4565b9601920196019592919092610b5c565b8060051b840135605e198536030181121561014657840190610bb6826113bb565b916000610bc660408301836113cf565b6040516338997b1160e01b81526001600160a01b039096166004870152928b0135602486015260606044860152606485018390529193918290608490869082840137828187840101528187601f80980116810103018183335af1908115610cc257600091610c50575b5060019250610c3e8289611449565b52610c498188611449565b5001610b30565b90503d806000833e610c628183611383565b810190898183031261014657805186811161014657829101938401121561014657825192610c8f84611401565b91610c9d6040519384611383565b8483528a858301011161014657600193610cbc918b80850191016110c1565b89610c2f565b6040513d6000823e3d90fd5b60608882018301528101610b23565b346101465760403660031901126101465760206080610cfa610fff565b610121610d05611015565b9161169f565b3461014657600036600319011261014657610d24611c20565b50610d2d611c20565b60405190610d3a8261134d565b601282526020917129b2b9b9b4b7b71025b2bc9028363ab3b4b760711b838201528152604051610d698161134d565b60058152640312e302e360dc1b8382015282820190815260405190610d8d8261134d565b6007825266416c6368656d7960c81b848301526040830191825260405191610db48361134d565b60138352724d6f646966792053657373696f6e204b65797360681b8584015260405190610de08261134d565b60019384835260005b878110610ee9575090610e27610e6d926060880194855260405190610e0d8261134d565b63ca9b97f360e01b8252898201528451906104198261141c565b50610e5c610e46604051978989525160808a8a015260a08901906110e4565b945194601f1995868983030160408a01526110e4565b9051848783030160608801526110e4565b9051948285830301608086015285519182815281810182808560051b8401019801946000925b858410610ea057888a0389f35b909192939495968580610ed68c8686869f030188526040838d5163ffffffff60e01b8151168452015191818582015201906110e4565b9b99019796959190910193019190610e93565b968093949597604051610efb8161134d565b60008152606083820152828288010152019694939296610de9565b346101465760403660031901126101465760043560ff811603610146576024356001600160401b03811161014657610f5290369060040161103f565b505060405163d623472560e01b8152600490fd5b346101465760403660031901126101465760206107ed610f84610fff565b610f8c611015565b90611843565b34610146576020366003190112610146576004359063ffffffff60e01b8216809203610146576020916368caf97960e01b8114908115610fd4575b5015158152f35b63ea15602d60e01b811491508115610fee575b5083610fcd565b6301ffc9a760e01b14905083610fe7565b600435906001600160a01b038216820361014657565b602435906001600160a01b038216820361014657565b35906001600160a01b038216820361014657565b9181601f84011215610146578235916001600160401b038311610146576020838186019501011161014657565b9060806003198301126101465760043560ff8116810361014657916024356001600160a01b0381168103610146579160443591606435906001600160401b038211610146576110bd9160040161103f565b9091565b60005b8381106110d45750506000910152565b81810151838201526020016110c4565b906020916110fd815180928185528580860191016110c1565b601f01601f1916010190565b9181601f84011215610146578235916001600160401b038311610146576020808501948460051b01011161014657565b6020908160408183019282815285518094520193019160005b828110611160575050505090565b83516001600160a01b031685529381019392810192600101611152565b346101465760a0366003190112610146576004356001600160a01b03811603610146576060366023190112610146576084356001600160401b038111610146576111cb90369060040161103f565b005b906003196060818401126101465760043560ff811681036101465792602435916001600160401b038311610146578261016092030112610146576004019060443590565b90815180825260208080930193019160005b828110611231575050505090565b83516001600160e01b03191685529381019392810192600101611223565b6005111561125957565b634e487b7160e01b600052602160045260246000fd5b60408091805161127e8161124f565b845260ff60208201511660208501520151910152565b90815180825260208080930193019160005b8281106112b4575050505090565b909192938260806001926112dc83895163ffffffff60e01b815116845201518483019061126f565b019501939291016112a6565b90815180825260208080930193019160005b828110611308575050505090565b909192938260e06001926113416040895163ffffffff851b8151168452611335868201518786019061126f565b0151608083019061126f565b019501939291016112fa565b604081019081106001600160401b0382111761076357604052565b606081019081106001600160401b0382111761076357604052565b90601f801991011681019081106001600160401b0382111761076357604052565b6001600160401b0381116107635760051b60200190565b356001600160a01b03811681036101465790565b903590601e198136030182121561014657018035906001600160401b0382116101465760200191813603831361014657565b6001600160401b03811161076357601f01601f191660200190565b8051156107b95760200190565b8051600110156107b95760400190565b8051600210156107b95760600190565b80518210156107b95760209160051b010190565b60018060a01b031690604051828152639cc6c92360e01b928360208301526000918260408201526060810191600193838580955260809385858220549284975b611532575b505050506114af846113a4565b966114bd6040519889611383565b848852601f196114cc866113a4565b013660208a0137878515611528575060405192835260208301528060408301528460608301905b8583106115035750505050505050565b8152858484205461ffff19811661151a858c611449565b5292019161fffe19166114f3565b9750505050505050565b90919296828816158061157a575b156115715782019661fffe198116906002161561156957815285832054915b908193929361149d565b50839161155f565b968291506114a2565b50871515611540565b60018060a01b031690604051828152639cc6c92360e01b928360208301526000918260408201526060810191600193838580955260809385858220549284975b61164e575b505050506115d5846113a4565b966115e36040519889611383565b848852601f196115f2866113a4565b013660208a0137878515611528575060405192835260208301528060408301528460608301905b8583106116295750505050505050565b8152858484205461ffff198116611640858c611449565b5292019161fffe1916611619565b909192968288161580611696575b1561168d5782019661fffe198116906002161561168557815285832054915b90819392936115c3565b50839161167b565b968291506115c8565b5087151561165c565b604080516001600160a01b039092168252639cc6c92360e01b602083015260009082015290565b805160005b8181106116d757505090565b806116e460019285611449565b5160601c6116f28286611449565b52016116cb565b91908110156107b95760061b0190565b91908110156107b95760051b0190565b9061ffff19169081156117a157604080516001600160a01b039092168252639cc6c92360e01b6020830152600090820152606081018281526080822080549092906117985760016080925220918254928315801561178e575b1561178257600193505555600190565b90600217905555600190565b5060018416611772565b50505050600090565b5050600090565b604080516001600160a01b039092168252639cc6c92360e01b602083015260009082015261fffe199261ffff199092169183168015801561183b575b61179857606082019081526080822091825493808686160361183057608092522091825480156118265760009461fffc60028316931691161717905555600190565b5050505050600090565b505050505050600090565b5082156117e4565b6108c661184f91611583565b805190600193846000905b84821061188257604051633c3629c360e11b81526001600160a01b0387166004820152602490fd5b6001600160a01b03806118958487611449565b5116908716146118c15750856001600160601b03196118b48386611449565b5160601b1691019061185a565b9450509350505090565b9291926118d782611401565b916118e56040519384611383565b829481845281830111610146578281602093846000960137010152565b60ff161561191c5760405163d623472560e01b8152600490fd5b60609061192b828201826113cf565b8060041161014657810190604091826003198383030112610146576001600160401b03600483013581811161014657830190826023830112156101465760048201359060249561197a836113a4565b9461198782519687611383565b8386528760208097019460051b8601019481861161014657888101945b868610611a7a57505050505050505001359160018060a01b039384841693848103610146576080917f19457468657265756d205369676e6564204d6573736167653a0a333200000000600052601c52603c60002092611a023361169f565b91811b6001600160601b031916908201522054611a23575b50505050600190565b611a3f611a3883610140611a459501906113cf565b36916118cb565b90611af4565b611a518194929461124f565b159283611a6e575b505050611a695738808080611a1a565b600090565b16149050388080611a59565b85358581116101465782018c602319828603011261014657845191611a9e83611368565b611aa98c830161102b565b8352604491828101358b8501526064810135908882116101465701928560438501121561014657611ae586858f958e97968897013591016118cb565b878201528152019501946119a4565b906041815114600014611b1e576110bd916020820151906060604084015193015160001a90611b28565b5050600090600290565b9291906fa2a8918ca85bafe22016d0b997e4df60600160ff1b038311611ba15791608094939160ff602094604051948552168484015260408301526060820152600093849182805260015afa15611b945781516001600160a01b03811615611b8e579190565b50600190565b50604051903d90823e3d90fd5b50505050600090600390565b604051906101a082018281106001600160401b038211176107635760405281610180606091828152826020820152826040820152828082015260006080820152600060a08201528260c08201528260e0820152826101008201528261012082015282610140820152826101608201520152565b60405190608082018281106001600160401b038211176107635760405260608083818152816020820152816040820152015256fea2646970667358221220e46e533a4c6295ca04a176e3e05fbb8cc4587224335aa50e4d8325e80aff428364736f6c63430008150033", + "nonce": "0x4e", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x8175ec950981e3c7254ab70faa68a8cb085dba2c107ed546a634e462ad48769f", + "transactionType": "CREATE", + "contractName": "SessionKeyPermissionsPlugin", + "contractAddress": "0x78462720341714Dcab90C6d5B9a47fAAA6fdADC6", + "function": null, + "arguments": null, + "transaction": { + "type": "0x02", + "from": "0x7f89ed1f3f0d52d303904101305471bca3cde710", + "gas": "0x2c33a6", + "value": "0x0", + "data": "0x6080806040523461001657612752908161001c8239f35b600080fdfe60808060405260048036101561001457600080fd5b60003560e01c91826301ffc9a71461150f575081630c7ecd84146107b35781631128186d146114c4578163118a5389146112a45781632d106419146111f457816346d60eb214610ff6578163676c3dc214610f0e578163684d07c514610fa35781636d61fe7014610ee75781637060f7f714610f135781637f42433814610f0e57816388e18ce414610eec5781638a91b0e314610ee757816398e2c27a14610eab578163991ef7f714610dea578163a1b9d5f414610da8578163af87348314610d87578163b7967b7114610d3d578163bb319893146107f357508063bc3b9246146107b8578063bfd151c1146107b3578063c776313014610272578063cfbb14ad14610217578063e5282cbd146101a65763e8a932df1461013457600080fd5b346101a15760803660031901126101a15761014d61157b565b610155611591565b61015d6115a7565b916064356001600160e01b0319811681036101a157602093838361019061018960ff97610195976118e9565b9283611932565b611ab9565b54166040519015158152f35b600080fd5b346101a15760403660031901126101a1576101dd6101c261157b565b6101ca611591565b906101d861018983836118e9565b6119ff565b805460681c60ff161561020d57600301546020906001600160a01b03165b6040516001600160a01b039091168152f35b50602060006101fb565b346101a15760603660031901126101a157604061025861023561157b565b61023d611591565b6102456115a7565b9161025361018983836118e9565b611a6d565b5460ff8251918181161515835260081c1615156020820152f35b346101a15760003660031901126101a15761028b611f5b565b50610294611f5b565b6102c66040516102a3816119a8565b6002815260403660208301376020830181905263ea15602d60e01b918291611fce565b526102d46020830151611ff1565b5261049361030a6040516102e7816119c3565b6003815260603660208301376040840181905263bb31989360e01b918291611fce565b5263991ef7f760e01b90816103226040860151611ff1565b5261046e637060f7f760e01b928361033d6040880151612001565b526103f260405161034d816119a8565b600281526000602082015260006040820152610367612044565b60e089015261039960405161037b8161195c565b86815282602082015260e08a01519061039382611fce565b52611fce565b506103c76040516103a98161195c565b84815282602082015260e08a0151906103c182611ff1565b52611ff1565b50604051906103d58261195c565b868252602082015260e0880151906103ec82612001565b52612001565b5061044860405193610403856119a8565b60028552600060208601526001604086015261041d612044565b610100890152604051906104308261195c565b81528460208201526101008801519061039382611fce565b50604051906104568261195c565b8152826020820152610100860151906103c182611ff1565b506040519161047c8361195c565b82526020820152610100830151906103ec82612001565b506040516104a08161195c565b6001815260005b6020811061078857506101208201526105036040516104c5816119a8565b600181526000602082015260006040820152604051906104e48261195c565b630c76670b60e21b825260208201526101208301519061039382611fce565b506040516105108161195c565b6001815260005b602081106107505750610160820152610596604051610535816119a8565b600181526001602082015260006040820152604051610553816119a8565b60008152600060208201526000604082015260405191610572836119a8565b630c76670b60e21b8352602083015260408201526101608301519061039382611fce565b506040518091602082526106036105ed6105d76105c184516101a060208801526101c08701906117b6565b6020850151868203601f190160408801526117b6565b6040840151858203601f190160608701526117b6565b6060830151848203601f190160808601526117b6565b6080820151151560a084015260a0820151151560c084015260c082015190601f198482030160e0850152815180825260208201916020808360051b8301019401926000915b8383106106f95787806106f5896106c261068e6106768c60e0850151601f1988830301610100890152611830565b610100840151868203601f1901610120880152611830565b6106ac6101208401519161014092601f198883030184890152611830565b90830151858203601f1901610160870152611830565b6106e06101608301519161018092601f198783030184880152611884565b910151838203601f19016101a0850152611884565b0390f35b9193959650919360208061073d600193601f19868203018752606060408b51878060a01b0381511684528581015115158685015201519181604082015201906117b6565b9701930193019092879695949293610648565b60209060405161075f816119a8565b6000815261076b612025565b83820152610777612025565b604082015282828501015201610517565b6020906040516107978161195c565b600081526107a3612025565b83820152828285010152016104a7565b611653565b346101a15760403660031901126101a15760a06107e46107d661157b565b6107de611591565b90611b34565b6107f160405180926116b7565bf35b346101a1576003196040368201126101a15761080d61157b565b6024928335906001600160401b03908183116101a157366023840112156101a15782810135958287116101a15780840195600588811b93368486890101116101a15761085e889a9697949a336118e9565b6108688982611932565b61087281336119ff565b9360005b888110610951575050505050506040519660209284848a01858b52526040808a01918a0101979560009360421981360301905b8786106108e3576001600160a01b038a16337f5e009edbae60e9d9dad056b2eba09cf2520596f0a9dbbabf9716ef5802e783768e8e038fa3005b909192939495969799603f198c82030185528a35838112156101a1578201906044878301359201918581116101a15780360383136101a15789828280600196849695859652848401376000828201840152601f01601f191601019c01999897919091019594019291906108a9565b6109668d829a9e989b9a871b8c01018d611c94565b9190858310610d2c576001600160e01b0319813581169390630e1adbe560e31b85016109ce5750809192935086116101a157838260209281010301126101a1578401359060038210156101a15760019160ff801989541691161787555b019b9798959b610876565b9193639e7345eb60e01b8103610a5e575080915086116101a157838360609281010301126101a1578188610a44610a31610a226044610a1b610a138c60019a016115bd565b95870161267a565b950161267a565b92868060a01b03168833611a6d565b92839060ff801983541691151516179055565b815461ff00191690151560081b61ff00161790555b6109c3565b630646f00160e11b8103610acf57508087116101a157848460609281010301126101a157610a8d8684016115bd565b8984013591821682036101a157610abe610a5992610aaf60446001970161267a565b92868060a01b03168833611ab9565b9060ff801983541691151516179055565b909150639a37b11360e01b8103610b4857508086116101a157838360409281010301126101a15781610b0f89610b08886001960161264d565b920161264d565b66ffffffffffff0089549165ffffffffffff60381b9060381b169260081b16906cffffffffffffffffffffffff001916171787556109c3565b909290632ce89bd760e21b8103610ba157508286116101a157610b758187600195610b8594019101612660565b9060068a019160028b0191612687565b875460ff60801b1916901560801b60ff60801b161787556109c3565b637b1f089360e01b8103610c3f575090918086116101a157838260609281010301126101a157610bd28582016115bd565b610bde6044830161264d565b906001600160a01b03168015610c2e576001939291610c01610c13928833611a6d565b9260028401918c878601920135612687565b815462ff0000191690151560101b62ff0000161790556109c3565b60405163c1ab6dc160e01b81528790fd5b63585ca4a560e01b8103610cb257508286116101a157610c698187600195610c8694019101612660565b90895460ff8160781c16610ca3575b50878a0191858b0191612687565b875460ff60701b191690151560701b60ff60701b161787556109c3565b60ff60781b19168a5538610c78565b90929063b85631d760e01b03610d2c578086116101a157838360209281010301126101a1576001916001600160a01b0390610cee9087016115bd565b1680610d055750865460ff60681b191687556109c3565b8260681b60ff60681b198954161788556003880190838060a01b03198254161790556109c3565b604051633e57645160e21b81528690fd5b346101a15760403660031901126101a15760ff610d5b6101c261157b565b5416604051906003811015610d7257602092508152f35b602183634e487b7160e01b6000525260246000fd5b346101a157610d9536611772565b505060405163d623472560e01b81529050fd5b346101a15760403660031901126101a15760c0610dd4610dc661157b565b610dce611591565b90611bd2565b610de160405180936116b7565b151560a0820152f35b346101a15760403660031901126101a157610e0361157b565b90610e0e82336118e9565b610e8a573360005260006020526040600020908154906000198214610e755750906001610e4092018091558233611c70565b602435906001600160a01b0316337f886c546ebd7ab8d6a406e5284ea019784616cf727fda33cbd4f7c82bfbb3c7cf600080a4005b601190634e487b7160e01b6000525260246000fd5b604051634f2034ad60e11b81526001600160a01b0390921690820152602490fd5b346101a15760403660031901126101a1576040610ec96101c261157b565b5465ffffffffffff825191818160081c16835260381c166020820152f35b611742565b346101a1576020610f06610eff36611772565b5090611cc6565b604051908152f35b6116f2565b346101a15760403660031901126101a157610f2c61157b565b610f34611591565b90610f3f81336118e9565b91610f4a8284611932565b610f79610f563361190e565b93600060018060a01b038095169586606082015260208151910120558233611c70565b1690337f67a108f883dad01f07fc4e8705cea092f645240361df1a3b23b550258bb80620600080a4005b346101a15760403660031901126101a157610fbf6101c261157b565b80549060ff8260781c16610fcf57005b60ff60781b199091168155600101805465ffffffffffff19164265ffffffffffff16179055005b346101a157611004366115fe565b60019460ff945090925083168414611069575b50505050604051602081018181106001600160401b03821117611054576106f5925060405260008152604051918291602083526020830190611677565b604183634e487b7160e01b6000525260246000fd5b8185116101a1578461107e92820191016120c7565b81519192916001600160a01b03918216916000919061109d84336118e9565b6110a78582611932565b6110b181336119ff565b968860005b8581106111595750505050505082845460801c161561110e575b50508154908160781c166110e5575b80611017565b60ff60781b1916815501805465ffffffffffff19164265ffffffffffff161790558180806110df565b61112190600685019060028601906124be565b1561112c57806110d0565b6040805163c8cfd93b60e01b8152338188019081526001600160a01b039093166020840152918291010390fd5b6111736111668285612011565b5197602089015190612201565b96611182868251168633611a6d565b8a815460101c16611198575b50500189906110b6565b819293506111b2876111bf93511660408501519033612554565b8d600283019201906124be565b156111cd5790818b9261118e565b5160405163160204d160e21b815233818e0152602481018990529085166044820152606490fd5b346101a15760603660031901126101a15760a061124961121261157b565b61121a611591565b6112226115a7565b9161122b611b09565b5061123961018983836118e9565b61124382826119ff565b50611a6d565b60ff815460101c1690600281015490600160038201549101549065ffffffffffff92604051946112788661198d565b1515855260208501526040840152818160301c1660608401521660808201526107f160405180926116b7565b346101a15760003660031901126101a1576112bd612093565b506112c6612093565b604051906112d38261195c565b601e82526020917f53657373696f6e204b6579205065726d697373696f6e7320506c7567696e000083820152815260405161130d8161195c565b60058152640312e302e360dc1b83820152828201908152604051906113318261195c565b6007825266416c6368656d7960c81b8483015260408301918252604051916113588361195c565b601e83527f4d6f646966792053657373696f6e204b6579205065726d697373696f6e730000858401526040519061138e8261195c565b60019384835260005b8781106114975750906113d561141b9260608801948552604051906113bb8261195c565b63bb31989360e01b82528982015284519061039382611fce565b5061140a6113f4604051978989525160808a8a015260a0890190611677565b945194601f1995868983030160408a0152611677565b905184878303016060880152611677565b9051948285830301608086015285519182815281810182808560051b8401019801946000925b85841061144e57888a0389f35b9091929394959685806114848c8686869f030188526040838d5163ffffffff60e01b815116845201519181858201520190611677565b9b99019796959190910193019190611441565b9680939495976040516114a98161195c565b60008152606083820152828288010152019694939296611397565b346101a15760403660031901126101a157803560ff8116036101a1576024356001600160401b0381116101a1576114fe90369083016115d1565b505060405163d623472560e01b8152fd5b90346101a15760203660031901126101a157359063ffffffff60e01b82168092036101a15760209163e7de0f3960e01b8114908115611550575b5015158152f35b63ea15602d60e01b81149150811561156a575b5083611549565b6301ffc9a760e01b14905083611563565b600435906001600160a01b03821682036101a157565b602435906001600160a01b03821682036101a157565b604435906001600160a01b03821682036101a157565b35906001600160a01b03821682036101a157565b9181601f840112156101a1578235916001600160401b0383116101a157602083818601950101116101a157565b9060806003198301126101a15760043560ff811681036101a157916024356001600160a01b03811681036101a1579160443591606435906001600160401b0382116101a15761164f916004016115d1565b9091565b346101a157611661366115fe565b5050505050600460405163d623472560e01b8152fd5b919082519283825260005b8481106116a3575050826000602080949584010152601f8019910116010190565b602081830181015184830182015201611682565b60809080511515835260208101516020840152604081015160408401528160608201519165ffffffffffff8093166060860152015116910152565b346101a15760a03660031901126101a1576004356001600160a01b038116036101a15760603660231901126101a1576084356001600160401b0381116101a1576117409036906004016115d1565b005b346101a15760203660031901126101a1576004356001600160401b0381116101a1576117409036906004016115d1565b906003196060818401126101a15760043560ff811681036101a15792602435916001600160401b0383116101a15782610160920301126101a1576004019060443590565b90815180825260208080930193019160005b8281106117d6575050505090565b83516001600160e01b031916855293810193928101926001016117c8565b8051600581101561181a576040918291845260ff60208201511660208501520151910152565b634e487b7160e01b600052602160045260246000fd5b90815180825260208080930193019160005b828110611850575050505090565b9091929382608060019261187883895163ffffffff60e01b81511684520151848301906117f4565b01950193929101611842565b90815180825260208080930193019160005b8281106118a4575050505090565b909192938260e06001926118dd6040895163ffffffff851b81511684526118d186820151878601906117f4565b015160808301906117f4565b01950193929101611896565b6118f29061190e565b6001600160a01b03909116606082015280516020909101205490565b90604051916080830160405260608352602083015263068076b960e21b6040830152565b1561193a5750565b60405163099326a960e01b81526001600160a01b039091166004820152602490fd5b604081019081106001600160401b0382111761197757604052565b634e487b7160e01b600052604160045260246000fd5b60a081019081106001600160401b0382111761197757604052565b606081019081106001600160401b0382111761197757604052565b608081019081106001600160401b0382111761197757604052565b90601f801991011681019081106001600160401b0382111761197757604052565b9060405190602082019260808301604052606083528352630b5ff94b60e11b908160408401526040519060208201928352602482015260248152611a42816119a8565b5190519060208110611a5b575b50606082015251902090565b6000199060200360031b1b1638611a4f565b90611ab6926040519260a0840160405260808452602084015263634c29f560e01b60408401526001600160601b03199060601b1690606083015260808201526020815191012090565b90565b6040805160a081018252608080825260208201938452630d50536f60e41b9282019290925260608101939093526001600160e01b03199094166001600160a01b0390931692909217908301522090565b60405190611b168261198d565b60006080838281528260208201528260408201528260608201520152565b90611b4f91611b41611b09565b506101d861018983836118e9565b805460801c60ff16611ba2576006810154906002600782015491015465ffffffffffff9160405193611b808561198d565b6001855260208501526040840152818160301c16606084015216608082015290565b50604051611baf8161198d565b600081526000602082015260006040820152600060608201526000608082015290565b90611bdf91611b41611b09565b9081549160ff808460781c169360701c16600014611c3f576004810154906001600582015491015465ffffffffffff9160405193611c1c8561198d565b6001855260208501526040840152818160301c1660608401521660808201529190565b50604051611c4c8161198d565b60008152600060208201526000604082015260006060820152600060808201529190565b611c799061190e565b6001600160a01b039091166060820152805160209091012055565b903590601e19813603018212156101a157018035906001600160401b0382116101a1576020019181360383136101a157565b60ff80911615611ce25760405163d623472560e01b8152600490fd5b611cef6060830183611c94565b90816004116101a1576004611d0792820191016120c7565b9091906001600160a01b03908116611d1f81336118e9565b91611d2a8284611932565b611d3483336119ff565b91825465ffffffffffff8160081c169660009080518015159783925b828410611f155750505050868260801c1615611edd575b5060701c8516611e2d575b50815495848760681c16611dc0575b50505060009015600014611dba57506001915b65ffffffffffff60d01b9060d01b169265ffffffffffff60a01b9060681b169116171790565b91611d94565b80610120611dcf920190611c94565b6bffffffffffffffffffffffff19913582811694929160148110611e18575b50505083611e02575b505050388080611d81565b6003919293500154169060601c14388080611df7565b60140360031b82901b16169250388080611dee565b9492611e9c90611e41610120890189611c94565b159050611ec257611e8d84611e88611e7d611e728c6080611e6a8d60035b1660a0840135612224565b910135612201565b60c08d013590612201565b60e08c013590612224565b6123c4565b919081611eba575b50946126ff565b94602087013560401c03611eb1575b38611d72565b60009250611eab565b905038611e95565b611e8d84611e88611e7d611e728c6080611e6a8d6001611e5f565b86919795611ef7611f06926006880190600289019061233d565b919081611f0d575b50966126ff565b9690611d67565b905038611eff565b909192986001908b611f2a6111668d86612011565b9682611f3d575b50509901929190611d50565b611f5492508560408c835116920151928a16612268565b8b38611f31565b604051906101a082018281106001600160401b038211176119775760405281610180606091828152826020820152826040820152828082015260006080820152600060a08201528260c08201528260e0820152826101008201528261012082015282610140820152826101608201520152565b805115611fdb5760200190565b634e487b7160e01b600052603260045260246000fd5b805160011015611fdb5760400190565b805160021015611fdb5760600190565b8051821015611fdb5760209160051b010190565b60405190612032826119a8565b60006040838281528260208201520152565b604090815191612053836119c3565b60038352600083815b6060811061206a5750505050565b60209084516120788161195c565b84815282612084612025565b8183015282850101520161205c565b60405190608082018281106001600160401b0382111761197757604052606080838181528160208201528160408201520152565b919060409081848203126101a1576001600160401b0384358181116101a157850190601f938385840112156101a157823593602095838611611977578560051b92805196612117898601896119de565b87528780880194870101958387116101a157888101945b87861061214957505050505050505090611ab69193016115bd565b85358781116101a157601f19916060918401808803840183136101a15785519261217d8e612176866119a8565b83016115bd565b8452818701358e850152810135908a82116101a157019187603f840112156101a1578c830135938a85116121ec576121bd8e8851928a88011601826119de565b848152888786860101116101a15760008e8681978a83980183860137830101528682015281520195019461212e565b60246000634e487b7160e01b81526041600452fd5b9190820180921161220e57565b634e487b7160e01b600052601160045260246000fd5b8181029291811591840414171561220e57565b906020825192015163ffffffff60e01b90818116936004811061225957505050565b60040360031b82901b16169150565b6001949361227590612237565b90612281848433611a6d565b90600381101561181a57806122cf57509091929394505460ff8116156122c65760081c60ff16156122be5760ff926122b99233611ab9565b541690565b505050600190565b50505050600090565b86146122dc575b50505050565b949091929394549060ff82161561231b575060081c60ff16156123135760ff926123069233611ab9565b54161590388080806122d6565b505050600090565b935050505090565b91909165ffffffffffff8080941691160191821161220e57565b9291909260008094549065ffffffffffff808360301c169160018601549554958501948086106123b8578361237757505050505011159190565b909294958092949611156000146123945750505050505060019190565b94969394929392116123b057505090611ab69160019416612323565b945092915050565b50965050505050508190565b9190916000809360018101549165ffffffffffff8084169360301c1692600583019384549160048501549480549060ff8260781c16948901958987106124af5784612424575050505050509061241e911115928254612201565b90559190565b90929680879c93969a95999c111560001461245c575050505050506001955560009260001461245757611ab69250612323565b505090565b909295508397949a96989193111590816124a6575b501561249b57505061248590600195612323565b60ff60781b19909616600160781b179055559190565b975095945050505050565b90501538612471565b9a505050505050505050508190565b9181549165ffffffffffff90818460301c16916001845494019485549382811592831561253c575b50505060001461251b5750508301928310908115612511575b5061250a5755600190565b5050600090565b90508211386124ff565b939150939182116122c65755421665ffffffffffff19825416179055600190565b612547935016612323565b81429116113882816124e6565b91906001600160e01b031961256883612237565b1663a9059cbb60e01b81036125915750509050604481511061258b576044015190565b50600090565b63095ea7b360e01b146125a657505050600090565b604482511061231357602482810151604051636eb1769f60e11b81526001600160a01b039586166004820152908516918101919091529260209184916044918391165afa9182156126415760009261260a575b50604401518181111561250a570390565b90916020823d8211612639575b81612624602093836119de565b810103126126365750519060446125f9565b80fd5b3d9150612617565b6040513d6000823e3d90fd5b359065ffffffffffff821682036101a157565b91908260409103126101a157611ab660208335930161264d565b359081151582036101a157565b92939260001981036126a0575050506000600181930155565b6001945565ffffffffffff908254918065ffffffffffff60301b8360301b16928365ffffffffffff60301b19861617865516156000146126ec5750505065ffffffffffff198154169055565b4216916001600160601b03191617179055565b9065ffffffffffff8082169083161115612717575090565b90509056fea264697066735822122065062352a5bc69f9af0e807ac2419d3184608490a0d9ceabe04c73defebd3f4964736f6c63430008150033", + "nonce": "0x4f", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "transactionHash": "0x09d250a7c7e7c8414232c10a3487fc301d10bd70b32b8f66c01db654f5471fbe", + "transactionIndex": "0x3", + "blockHash": "0x68ee21972d7928cb103cb8c82274f34fb012e206a92156cebfc0155116ec31e5", + "blockNumber": "0x486928", + "from": "0x7f89Ed1F3F0d52d303904101305471bca3cde710", + "to": null, + "cumulativeGasUsed": "0x52ee92", + "gasUsed": "0x506f27", + "contractAddress": "0xb2b748c2557c552B8636862E41aB3649319dD045", + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb8494eb8" + }, + { + "transactionHash": "0x80c2f62037e084f594ce6d0be7de0119d62e5d9faefe5f903515d1b76d9fea40", + "transactionIndex": "0x4", + "blockHash": "0x68ee21972d7928cb103cb8c82274f34fb012e206a92156cebfc0155116ec31e5", + "blockNumber": "0x486928", + "from": "0x7f89Ed1F3F0d52d303904101305471bca3cde710", + "to": null, + "cumulativeGasUsed": "0x6fcc34", + "gasUsed": "0x1cdda2", + "contractAddress": "0x56bC629F342821FBe91C5273880792dFECBE7920", + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb8494eb8" + }, + { + "transactionHash": "0x0f13a3b7e8528a2fb6f1b4ceb43330feab35d56a3ffc7ffc0cbe8bd16ea4d7ba", + "transactionIndex": "0x5", + "blockHash": "0x68ee21972d7928cb103cb8c82274f34fb012e206a92156cebfc0155116ec31e5", + "blockNumber": "0x486928", + "from": "0x7f89Ed1F3F0d52d303904101305471bca3cde710", + "to": null, + "cumulativeGasUsed": "0x7be413", + "gasUsed": "0xc17df", + "contractAddress": "0xa81C0AEaB22b21b4da8d8728063f6570384b48C9", + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb8494eb8" + }, + { + "transactionHash": "0xcefba29b52d19365a4b859228052e7415eed0576786ea4330dbe977cf2dbf7cc", + "transactionIndex": "0x6", + "blockHash": "0x68ee21972d7928cb103cb8c82274f34fb012e206a92156cebfc0155116ec31e5", + "blockNumber": "0x486928", + "from": "0x7f89Ed1F3F0d52d303904101305471bca3cde710", + "to": null, + "cumulativeGasUsed": "0x8d04f8", + "gasUsed": "0x1120e5", + "contractAddress": "0xFD14c78640d72f73CC88238E2f7Df3273Ee84043", + "logs": [ + { + "address": "0xFD14c78640d72f73CC88238E2f7Df3273Ee84043", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000007f89ed1f3f0d52d303904101305471bca3cde710" + ], + "data": "0x", + "blockHash": "0x68ee21972d7928cb103cb8c82274f34fb012e206a92156cebfc0155116ec31e5", + "blockNumber": "0x486928", + "transactionHash": "0xcefba29b52d19365a4b859228052e7415eed0576786ea4330dbe977cf2dbf7cc", + "transactionIndex": "0x6", + "logIndex": "0x2", + "removed": false + }, + { + "address": "0xFD14c78640d72f73CC88238E2f7Df3273Ee84043", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000007f89ed1f3f0d52d303904101305471bca3cde710", + "0x0000000000000000000000007f89ed1f3f0d52d303904101305471bca3cde710" + ], + "data": "0x", + "blockHash": "0x68ee21972d7928cb103cb8c82274f34fb012e206a92156cebfc0155116ec31e5", + "blockNumber": "0x486928", + "transactionHash": "0xcefba29b52d19365a4b859228052e7415eed0576786ea4330dbe977cf2dbf7cc", + "transactionIndex": "0x6", + "logIndex": "0x3", + "removed": false + } + ], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000400000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001010000000000000000000000000000000100020000000000000000000800000000000000000000000000000000400000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000001000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb8494eb8" + }, + { + "transactionHash": "0xb2179964a1836d4510adb53395bbf338881895fb1943e077bfffdea583a5fa57", + "transactionIndex": "0x7", + "blockHash": "0x68ee21972d7928cb103cb8c82274f34fb012e206a92156cebfc0155116ec31e5", + "blockNumber": "0x486928", + "from": "0x7f89Ed1F3F0d52d303904101305471bca3cde710", + "to": "0xFD14c78640d72f73CC88238E2f7Df3273Ee84043", + "cumulativeGasUsed": "0x8e42d1", + "gasUsed": "0x13dd9", + "contractAddress": null, + "logs": [ + { + "address": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", + "topics": [ + "0xa5ae833d0bb1dcd632d98a8b70973e8516812898e19bf27b70071ebc8dc52c01", + "0x000000000000000000000000fd14c78640d72f73cc88238e2f7df3273ee84043" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000015180", + "blockHash": "0x68ee21972d7928cb103cb8c82274f34fb012e206a92156cebfc0155116ec31e5", + "blockNumber": "0x486928", + "transactionHash": "0xb2179964a1836d4510adb53395bbf338881895fb1943e077bfffdea583a5fa57", + "transactionIndex": "0x7", + "logIndex": "0x4", + "removed": false + } + ], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000040000000000000000000000000000000000000000000200000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000100000000200000000000000000000000000200000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb8494eb8" + }, + { + "transactionHash": "0x5cb7347b960e28bd3bf3a4958f438cd477ed9a2bdf2c2862ade7b33ce951b14c", + "transactionIndex": "0x8", + "blockHash": "0x68ee21972d7928cb103cb8c82274f34fb012e206a92156cebfc0155116ec31e5", + "blockNumber": "0x486928", + "from": "0x7f89Ed1F3F0d52d303904101305471bca3cde710", + "to": null, + "cumulativeGasUsed": "0xa053b6", + "gasUsed": "0x1210e5", + "contractAddress": "0x22322E35c1850F26DD54Ed8F59a27C1c79847A15", + "logs": [ + { + "address": "0x22322E35c1850F26DD54Ed8F59a27C1c79847A15", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000007f89ed1f3f0d52d303904101305471bca3cde710" + ], + "data": "0x", + "blockHash": "0x68ee21972d7928cb103cb8c82274f34fb012e206a92156cebfc0155116ec31e5", + "blockNumber": "0x486928", + "transactionHash": "0x5cb7347b960e28bd3bf3a4958f438cd477ed9a2bdf2c2862ade7b33ce951b14c", + "transactionIndex": "0x8", + "logIndex": "0x5", + "removed": false + }, + { + "address": "0x22322E35c1850F26DD54Ed8F59a27C1c79847A15", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000007f89ed1f3f0d52d303904101305471bca3cde710", + "0x0000000000000000000000007f89ed1f3f0d52d303904101305471bca3cde710" + ], + "data": "0x", + "blockHash": "0x68ee21972d7928cb103cb8c82274f34fb012e206a92156cebfc0155116ec31e5", + "blockNumber": "0x486928", + "transactionHash": "0x5cb7347b960e28bd3bf3a4958f438cd477ed9a2bdf2c2862ade7b33ce951b14c", + "transactionIndex": "0x8", + "logIndex": "0x6", + "removed": false + } + ], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000100020000000000001000000800000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000001000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb8494eb8" + }, + { + "transactionHash": "0x045e474abf86398ebbf1814e8efd5407a1800a52d6d3bc30be6e0e7d2536db54", + "transactionIndex": "0x9", + "blockHash": "0x68ee21972d7928cb103cb8c82274f34fb012e206a92156cebfc0155116ec31e5", + "blockNumber": "0x486928", + "from": "0x7f89Ed1F3F0d52d303904101305471bca3cde710", + "to": "0x22322E35c1850F26DD54Ed8F59a27C1c79847A15", + "cumulativeGasUsed": "0xa191a5", + "gasUsed": "0x13def", + "contractAddress": null, + "logs": [ + { + "address": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", + "topics": [ + "0xa5ae833d0bb1dcd632d98a8b70973e8516812898e19bf27b70071ebc8dc52c01", + "0x00000000000000000000000022322e35c1850f26dd54ed8f59a27c1c79847a15" + ], + "data": "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000015180", + "blockHash": "0x68ee21972d7928cb103cb8c82274f34fb012e206a92156cebfc0155116ec31e5", + "blockNumber": "0x486928", + "transactionHash": "0x045e474abf86398ebbf1814e8efd5407a1800a52d6d3bc30be6e0e7d2536db54", + "transactionIndex": "0x9", + "logIndex": "0x7", + "removed": false + } + ], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000001000000040000000000000000000000000000100000000000000000000000020000000000200000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb8494eb8" + }, + { + "transactionHash": "0x8d523325b3f21dedb6e264a1da4295ef0e00cc59c3d2c26d6dcea079f6b00e47", + "transactionIndex": "0xa", + "blockHash": "0x68ee21972d7928cb103cb8c82274f34fb012e206a92156cebfc0155116ec31e5", + "blockNumber": "0x486928", + "from": "0x7f89Ed1F3F0d52d303904101305471bca3cde710", + "to": null, + "cumulativeGasUsed": "0xba7a1e", + "gasUsed": "0x18e879", + "contractAddress": "0x588dCE36DdeB0e40320AD1d9909a1f86053D7Df1", + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb8494eb8" + }, + { + "transactionHash": "0x8175ec950981e3c7254ab70faa68a8cb085dba2c107ed546a634e462ad48769f", + "transactionIndex": "0xb", + "blockHash": "0x68ee21972d7928cb103cb8c82274f34fb012e206a92156cebfc0155116ec31e5", + "blockNumber": "0x486928", + "from": "0x7f89Ed1F3F0d52d303904101305471bca3cde710", + "to": null, + "cumulativeGasUsed": "0xdc7cef", + "gasUsed": "0x2202d1", + "contractAddress": "0x78462720341714Dcab90C6d5B9a47fAAA6fdADC6", + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb8494eb8" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1700676229, + "chain": 11155111, + "multi": false, + "commit": "0e3fd1e" +} \ No newline at end of file diff --git a/broadcast/Deploy.s.sol/11155111/run-1700676621.json b/broadcast/Deploy.s.sol/11155111/run-1700676621.json new file mode 100644 index 00000000..f08b4d90 --- /dev/null +++ b/broadcast/Deploy.s.sol/11155111/run-1700676621.json @@ -0,0 +1,55 @@ +{ + "transactions": [ + { + "hash": "0x1cb319cb522dc9657527409126a2b8d612628b853d89e35c218391e3f7cb2171", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0xFD14c78640d72f73CC88238E2f7Df3273Ee84043", + "function": null, + "arguments": null, + "transaction": { + "type": "0x02", + "from": "0x7f89ed1f3f0d52d303904101305471bca3cde710", + "to": "0xfd14c78640d72f73cc88238e2f7df3273ee84043", + "gas": "0xefd8", + "value": "0xde0b6b3a763ffff", + "data": "0xfbb1c3d400000000000000000000000000000000000000000000000000000000000151800000000000000000000000000000000000000000000000000de0b6b3a763ffff", + "nonce": "0x50", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x286fc67e8439e61fac68ba4fd32339f99031f95cbc4715bcc784df116f7e6f2d", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0x22322E35c1850F26DD54Ed8F59a27C1c79847A15", + "function": null, + "arguments": null, + "transaction": { + "type": "0x02", + "from": "0x7f89ed1f3f0d52d303904101305471bca3cde710", + "to": "0x22322e35c1850f26dd54ed8f59a27c1c79847a15", + "gas": "0xfe14", + "value": "0xde0b6b3a763ffff", + "data": "0xfbb1c3d400000000000000000000000000000000000000000000000000000000000151800000000000000000000000000000000000000000000000000de0b6b3a763ffff", + "nonce": "0x51", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [], + "libraries": [], + "pending": [ + "0x1cb319cb522dc9657527409126a2b8d612628b853d89e35c218391e3f7cb2171", + "0x286fc67e8439e61fac68ba4fd32339f99031f95cbc4715bcc784df116f7e6f2d" + ], + "returns": {}, + "timestamp": 1700676621, + "chain": 11155111, + "multi": false, + "commit": "1ff992b" +} \ No newline at end of file diff --git a/broadcast/Deploy.s.sol/11155111/run-1700676627.json b/broadcast/Deploy.s.sol/11155111/run-1700676627.json new file mode 100644 index 00000000..ac8ce1b6 --- /dev/null +++ b/broadcast/Deploy.s.sol/11155111/run-1700676627.json @@ -0,0 +1,115 @@ +{ + "transactions": [ + { + "hash": "0x1cb319cb522dc9657527409126a2b8d612628b853d89e35c218391e3f7cb2171", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0xFD14c78640d72f73CC88238E2f7Df3273Ee84043", + "function": null, + "arguments": null, + "transaction": { + "type": "0x02", + "from": "0x7f89ed1f3f0d52d303904101305471bca3cde710", + "to": "0xfd14c78640d72f73cc88238e2f7df3273ee84043", + "gas": "0xefd8", + "value": "0xde0b6b3a763ffff", + "data": "0xfbb1c3d400000000000000000000000000000000000000000000000000000000000151800000000000000000000000000000000000000000000000000de0b6b3a763ffff", + "nonce": "0x50", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x286fc67e8439e61fac68ba4fd32339f99031f95cbc4715bcc784df116f7e6f2d", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0x22322E35c1850F26DD54Ed8F59a27C1c79847A15", + "function": null, + "arguments": null, + "transaction": { + "type": "0x02", + "from": "0x7f89ed1f3f0d52d303904101305471bca3cde710", + "to": "0x22322e35c1850f26dd54ed8f59a27c1c79847a15", + "gas": "0xfe14", + "value": "0xde0b6b3a763ffff", + "data": "0xfbb1c3d400000000000000000000000000000000000000000000000000000000000151800000000000000000000000000000000000000000000000000de0b6b3a763ffff", + "nonce": "0x51", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "transactionHash": "0x1cb319cb522dc9657527409126a2b8d612628b853d89e35c218391e3f7cb2171", + "transactionIndex": "0xa", + "blockHash": "0xd92d201f912d4a79c1aad303379572ef0adb9cfeae102d299cf08a1331bdd2af", + "blockNumber": "0x486946", + "from": "0x7f89Ed1F3F0d52d303904101305471bca3cde710", + "to": "0xFD14c78640d72f73CC88238E2f7Df3273Ee84043", + "cumulativeGasUsed": "0x88d3a", + "gasUsed": "0xada5", + "contractAddress": null, + "logs": [ + { + "address": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", + "topics": [ + "0xa5ae833d0bb1dcd632d98a8b70973e8516812898e19bf27b70071ebc8dc52c01", + "0x000000000000000000000000fd14c78640d72f73cc88238e2f7df3273ee84043" + ], + "data": "0x0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000015180", + "blockHash": "0xd92d201f912d4a79c1aad303379572ef0adb9cfeae102d299cf08a1331bdd2af", + "blockNumber": "0x486946", + "transactionHash": "0x1cb319cb522dc9657527409126a2b8d612628b853d89e35c218391e3f7cb2171", + "transactionIndex": "0xa", + "logIndex": "0x3", + "removed": false + } + ], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000040000000000000000000000000000000000000000000200000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000100000000200000000000000000000000000200000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb6d6ad31" + }, + { + "transactionHash": "0x286fc67e8439e61fac68ba4fd32339f99031f95cbc4715bcc784df116f7e6f2d", + "transactionIndex": "0xb", + "blockHash": "0xd92d201f912d4a79c1aad303379572ef0adb9cfeae102d299cf08a1331bdd2af", + "blockNumber": "0x486946", + "from": "0x7f89Ed1F3F0d52d303904101305471bca3cde710", + "to": "0x22322E35c1850F26DD54Ed8F59a27C1c79847A15", + "cumulativeGasUsed": "0x93af5", + "gasUsed": "0xadbb", + "contractAddress": null, + "logs": [ + { + "address": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", + "topics": [ + "0xa5ae833d0bb1dcd632d98a8b70973e8516812898e19bf27b70071ebc8dc52c01", + "0x00000000000000000000000022322e35c1850f26dd54ed8f59a27c1c79847a15" + ], + "data": "0x0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000015180", + "blockHash": "0xd92d201f912d4a79c1aad303379572ef0adb9cfeae102d299cf08a1331bdd2af", + "blockNumber": "0x486946", + "transactionHash": "0x286fc67e8439e61fac68ba4fd32339f99031f95cbc4715bcc784df116f7e6f2d", + "transactionIndex": "0xb", + "logIndex": "0x4", + "removed": false + } + ], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000001000000040000000000000000000000000000100000000000000000000000020000000000200000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb6d6ad31" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1700676627, + "chain": 11155111, + "multi": false, + "commit": "1ff992b" +} \ No newline at end of file diff --git a/broadcast/Deploy.s.sol/11155111/run-latest.json b/broadcast/Deploy.s.sol/11155111/run-latest.json new file mode 100644 index 00000000..ac8ce1b6 --- /dev/null +++ b/broadcast/Deploy.s.sol/11155111/run-latest.json @@ -0,0 +1,115 @@ +{ + "transactions": [ + { + "hash": "0x1cb319cb522dc9657527409126a2b8d612628b853d89e35c218391e3f7cb2171", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0xFD14c78640d72f73CC88238E2f7Df3273Ee84043", + "function": null, + "arguments": null, + "transaction": { + "type": "0x02", + "from": "0x7f89ed1f3f0d52d303904101305471bca3cde710", + "to": "0xfd14c78640d72f73cc88238e2f7df3273ee84043", + "gas": "0xefd8", + "value": "0xde0b6b3a763ffff", + "data": "0xfbb1c3d400000000000000000000000000000000000000000000000000000000000151800000000000000000000000000000000000000000000000000de0b6b3a763ffff", + "nonce": "0x50", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + }, + { + "hash": "0x286fc67e8439e61fac68ba4fd32339f99031f95cbc4715bcc784df116f7e6f2d", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0x22322E35c1850F26DD54Ed8F59a27C1c79847A15", + "function": null, + "arguments": null, + "transaction": { + "type": "0x02", + "from": "0x7f89ed1f3f0d52d303904101305471bca3cde710", + "to": "0x22322e35c1850f26dd54ed8f59a27c1c79847a15", + "gas": "0xfe14", + "value": "0xde0b6b3a763ffff", + "data": "0xfbb1c3d400000000000000000000000000000000000000000000000000000000000151800000000000000000000000000000000000000000000000000de0b6b3a763ffff", + "nonce": "0x51", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "transactionHash": "0x1cb319cb522dc9657527409126a2b8d612628b853d89e35c218391e3f7cb2171", + "transactionIndex": "0xa", + "blockHash": "0xd92d201f912d4a79c1aad303379572ef0adb9cfeae102d299cf08a1331bdd2af", + "blockNumber": "0x486946", + "from": "0x7f89Ed1F3F0d52d303904101305471bca3cde710", + "to": "0xFD14c78640d72f73CC88238E2f7Df3273Ee84043", + "cumulativeGasUsed": "0x88d3a", + "gasUsed": "0xada5", + "contractAddress": null, + "logs": [ + { + "address": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", + "topics": [ + "0xa5ae833d0bb1dcd632d98a8b70973e8516812898e19bf27b70071ebc8dc52c01", + "0x000000000000000000000000fd14c78640d72f73cc88238e2f7df3273ee84043" + ], + "data": "0x0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000015180", + "blockHash": "0xd92d201f912d4a79c1aad303379572ef0adb9cfeae102d299cf08a1331bdd2af", + "blockNumber": "0x486946", + "transactionHash": "0x1cb319cb522dc9657527409126a2b8d612628b853d89e35c218391e3f7cb2171", + "transactionIndex": "0xa", + "logIndex": "0x3", + "removed": false + } + ], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000040000000000000000000000000000000000000000000200000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000100000000200000000000000000000000000200000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb6d6ad31" + }, + { + "transactionHash": "0x286fc67e8439e61fac68ba4fd32339f99031f95cbc4715bcc784df116f7e6f2d", + "transactionIndex": "0xb", + "blockHash": "0xd92d201f912d4a79c1aad303379572ef0adb9cfeae102d299cf08a1331bdd2af", + "blockNumber": "0x486946", + "from": "0x7f89Ed1F3F0d52d303904101305471bca3cde710", + "to": "0x22322E35c1850F26DD54Ed8F59a27C1c79847A15", + "cumulativeGasUsed": "0x93af5", + "gasUsed": "0xadbb", + "contractAddress": null, + "logs": [ + { + "address": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", + "topics": [ + "0xa5ae833d0bb1dcd632d98a8b70973e8516812898e19bf27b70071ebc8dc52c01", + "0x00000000000000000000000022322e35c1850f26dd54ed8f59a27c1c79847a15" + ], + "data": "0x0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000015180", + "blockHash": "0xd92d201f912d4a79c1aad303379572ef0adb9cfeae102d299cf08a1331bdd2af", + "blockNumber": "0x486946", + "transactionHash": "0x286fc67e8439e61fac68ba4fd32339f99031f95cbc4715bcc784df116f7e6f2d", + "transactionIndex": "0xb", + "logIndex": "0x4", + "removed": false + } + ], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000001000000040000000000000000000000000000100000000000000000000000020000000000200000000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb6d6ad31" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1700676627, + "chain": 11155111, + "multi": false, + "commit": "1ff992b" +} \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index 71ad0e28..2d276cd1 100644 --- a/foundry.toml +++ b/foundry.toml @@ -36,8 +36,9 @@ line_length = 115 wrap_comments = true [rpc_endpoints] -mainnet = "${RPC_URL_MAINNET}" -goerli = "${RPC_URL_GOERLI}" +mainnet = "${MAINNET_RPC_URL}" +sepolia = "${SEPOLIA_RPC_URL}" +goerli = "${GOERLI_RPC_URL}" [etherscan] mainnet = { key = "${ETHERSCAN_API_KEY}" } diff --git a/migrations/sepolia/contracts.json b/migrations/sepolia/contracts.json new file mode 100644 index 00000000..fe698daf --- /dev/null +++ b/migrations/sepolia/contracts.json @@ -0,0 +1,25 @@ +{ + "EntryPoint": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", + "Prod": { + "MSCA Impl": "", + "MultiOwnerPlugin": "", + "TokenReceiverPlugin": "", + "MultiOwnerMSCAFactory": "", + "MultiOwnerTokenReceiverMSCAFactory": "", + "SessionKeyPlugin": "", + "SessionKeyPermissionsPlugin:": "", + "MultiOwnerMSCAFactory Owner": "", + "MultiOwnerTokenReceiverMSCAFactory Owner": "" + }, + "Staging": { + "MSCA Impl": "0xb2b748c2557c552B8636862E41aB3649319dD045", + "MultiOwnerPlugin": "0x56bC629F342821FBe91C5273880792dFECBE7920", + "TokenReceiverPlugin": "0xa81C0AEaB22b21b4da8d8728063f6570384b48C9", + "MultiOwnerMSCAFactory": "0xFD14c78640d72f73CC88238E2f7Df3273Ee84043", + "MultiOwnerTokenReceiverMSCAFactory": "0x22322E35c1850F26DD54Ed8F59a27C1c79847A15", + "SessionKeyPlugin": "0x588dCE36DdeB0e40320AD1d9909a1f86053D7Df1", + "SessionKeyPermissionsPlugin:": "0x78462720341714Dcab90C6d5B9a47fAAA6fdADC6", + "MultiOwnerMSCAFactory Owner": "0x7f89Ed1F3F0d52d303904101305471bca3cde710", + "MultiOwnerTokenReceiverMSCAFactory Owner": "0x7f89Ed1F3F0d52d303904101305471bca3cde710" + } +} diff --git a/script/Counter.s.sol b/script/Counter.s.sol deleted file mode 100644 index fb4224a5..00000000 --- a/script/Counter.s.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.14; - -import "forge-std/Script.sol"; - -contract CounterScript is Script { - function setUp() public {} - - function run() public { - vm.broadcast(); - } -} diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol new file mode 100644 index 00000000..844b16cb --- /dev/null +++ b/script/Deploy.s.sol @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.21; + +import {console} from "forge-std/Test.sol"; +import {Script} from "forge-std/Script.sol"; + +import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol"; + +import {IEntryPoint as IMSCAEntryPoint} from "../src/interfaces/erc4337/IEntryPoint.sol"; + +import {UpgradeableModularAccount} from "../src/account/UpgradeableModularAccount.sol"; + +import {MultiOwnerMSCAFactory} from "../src/factory/MultiOwnerMSCAFactory.sol"; +import {MultiOwnerTokenReceiverMSCAFactory} from "../src/factory/MultiOwnerTokenReceiverMSCAFactory.sol"; + +import {BasePlugin} from "../src/plugins/BasePlugin.sol"; +import {MultiOwnerPlugin} from "../src/plugins/owner/MultiOwnerPlugin.sol"; +import {TokenReceiverPlugin} from "../src/plugins/TokenReceiverPlugin.sol"; +import {SessionKeyPlugin} from "../src/plugins/session/SessionKeyPlugin.sol"; +import {SessionKeyPermissionsPlugin} from "../src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol"; + +contract Deploy is Script { + // Load entrypoint from env + address public entryPointAddr = vm.envAddress("ENTRYPOINT"); + IMSCAEntryPoint public entryPoint = IMSCAEntryPoint(payable(entryPointAddr)); + + // Load factory owner from env + address public owner = vm.envAddress("OWNER"); + + // Load core contract, if not in env, deploy new contract + address public mscaImpl = vm.envOr("MSCA_IMPL", address(0)); + address public ownerFactoryAddr = vm.envOr("OWNER_FACTORY", address(0)); + address public ownerAndTokenReceiverFactoryAddr = vm.envOr("OWNER_TOKEN_RECEIVER_FACTORY", address(0)); + MultiOwnerMSCAFactory ownerFactory; + MultiOwnerTokenReceiverMSCAFactory ownerAndTokenReceiverFactory; + + // Load plugins contract, if not in env, deploy new contract + address public multiOwnerPlugin = vm.envOr("OWNER_PLUGIN", address(0)); + bytes32 public multiOwnerPluginManifestHash; + address public tokenReceiverPlugin = vm.envOr("TOKEN_RECEIVER_PLUGIN", address(0)); + bytes32 public tokenReceiverPluginManifestHash; + address public sessionKeyPlugin = vm.envOr("SESSION_KEY_PLUGIN", address(0)); + address public sessionKeyPermissionsPlugin = vm.envOr("SESSION_KEY_PERMS_PLUGIN", address(0)); + + function run() public { + console.log("******** Deploying *********"); + console.log("Chain: ", block.chainid); + console.log("EP: ", entryPointAddr); + console.log("Factory owner: ", owner); + + uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); + vm.startBroadcast(deployerPrivateKey); + + // Deploy msca impl + if (mscaImpl == address(0)) { + UpgradeableModularAccount msca = new UpgradeableModularAccount(entryPoint); + mscaImpl = address(msca); + console.log("New MSCA impl: ", mscaImpl); + } else { + console.log("Exist MSCA impl: ", mscaImpl); + } + + // Deploy multi owner plugin, and set plugin hash + if (multiOwnerPlugin == address(0)) { + MultiOwnerPlugin mop = new MultiOwnerPlugin(); + multiOwnerPlugin = address(mop); + console.log("New MultiOwnerPlugin: ", multiOwnerPlugin); + } else { + console.log("Exist MultiOwnerPlugin: ", multiOwnerPlugin); + } + multiOwnerPluginManifestHash = keccak256(abi.encode(BasePlugin(multiOwnerPlugin).pluginManifest())); + + // Deploy multi owner plugin, and set plugin hash + if (tokenReceiverPlugin == address(0)) { + TokenReceiverPlugin trp = new TokenReceiverPlugin(); + tokenReceiverPlugin = address(trp); + console.log("New TokenReceiverPlugin: ", tokenReceiverPlugin); + } else { + console.log("Exist TokenReceiverPlugin: ", tokenReceiverPlugin); + } + tokenReceiverPluginManifestHash = keccak256(abi.encode(BasePlugin(tokenReceiverPlugin).pluginManifest())); + + // Deploy MultiOwnerMSCAFactory, and add stake with EP + { + if (ownerFactoryAddr == address(0)) { + ownerFactory = + new MultiOwnerMSCAFactory(owner, multiOwnerPlugin, mscaImpl, multiOwnerPluginManifestHash, entryPoint); + + ownerFactoryAddr = address(ownerFactory); + console.log("New MultiOwnerMSCAFactory: ", ownerFactoryAddr); + } else { + console.log("Exist MultiOwnerMSCAFactory: ", ownerFactoryAddr); + } + _addStakeForFactory(ownerFactoryAddr, entryPoint); + } + + // Deploy MultiOwnerTokenReceiverMSCAFactory, and add stake with EP + if (ownerAndTokenReceiverFactoryAddr == address(0)) { + ownerAndTokenReceiverFactory = + new MultiOwnerTokenReceiverMSCAFactory(owner, multiOwnerPlugin, tokenReceiverPlugin, mscaImpl, multiOwnerPluginManifestHash, tokenReceiverPluginManifestHash, entryPoint); + + ownerAndTokenReceiverFactoryAddr = address(ownerAndTokenReceiverFactory); + console.log("New MultiOwnerTokenReceiverMSCAFactory: ", ownerAndTokenReceiverFactoryAddr); + } else { + console.log("Exist MultiOwnerTokenReceiverMSCAFactory: ", ownerAndTokenReceiverFactoryAddr); + } + _addStakeForFactory(ownerAndTokenReceiverFactoryAddr, entryPoint); + + // Deploy SessionKeyPlugin impl + if (sessionKeyPlugin == address(0)) { + SessionKeyPlugin skp = new SessionKeyPlugin(); + sessionKeyPlugin = address(skp); + console.log("New SessionKeyPlugin: ", sessionKeyPlugin); + } else { + console.log("Exist SessionKeyPlugin: ", sessionKeyPlugin); + } + + // Deploy SessionKeyPermissionsPlugin impl + if (sessionKeyPermissionsPlugin == address(0)) { + SessionKeyPermissionsPlugin skpp = new SessionKeyPermissionsPlugin(); + sessionKeyPermissionsPlugin = address(skpp); + console.log("New SessionKeyPermissionsPlugin: ", sessionKeyPermissionsPlugin); + } else { + console.log("Exist SessionKeyPermissionsPlugin: ", sessionKeyPermissionsPlugin); + } + + console.log("******** Deploy Done! *********"); + vm.stopBroadcast(); + } + + function _addStakeForFactory(address factoryAddr, IMSCAEntryPoint entryPoint) internal { + uint32 unstakeDelaySec = uint32(vm.envOr("UNSTAKE_DELAY_SEC", uint32(60))); + uint256 requiredStakeAmount = vm.envUint("REQUIRED_STAKE_AMOUNT") * 1 ether; + uint256 currentStakedAmount = IEntryPoint(address(entryPoint)).getDepositInfo(factoryAddr).stake; + uint256 stakeAmount = requiredStakeAmount - currentStakedAmount; + // since all factory share the same addStake method, it does not matter which contract we use to cast the + // address + MultiOwnerMSCAFactory(payable(factoryAddr)).addStake{value: stakeAmount}(unstakeDelaySec, stakeAmount); + console.log("******** Add Stake Verify *********"); + console.log("Staked factory: ", factoryAddr); + console.log("Stake amount: ", IEntryPoint(address(entryPoint)).getDepositInfo(factoryAddr).stake); + console.log( + "Unstake delay: ", IEntryPoint(address(entryPoint)).getDepositInfo(factoryAddr).unstakeDelaySec + ); + console.log("******** Stake Verify Done *********"); + } +} From 2273cc9a5e64dd025596e224fb7bab358a37956a Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Tue, 12 Dec 2023 08:36:39 -0800 Subject: [PATCH 003/106] fix: [spearbit-12] Typos (#12) --- src/account/AccountExecutor.sol | 2 +- src/account/AccountStorageInitializable.sol | 2 +- src/account/UpgradeableModularAccount.sol | 6 +++--- src/interfaces/IPluginExecutor.sol | 2 +- src/libraries/AccountStorageV1.sol | 2 +- .../session/permissions/SessionKeyPermissionsPlugin.sol | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/account/AccountExecutor.sol b/src/account/AccountExecutor.sol index 7f4e8ebd..6e29ee64 100644 --- a/src/account/AccountExecutor.sol +++ b/src/account/AccountExecutor.sol @@ -162,7 +162,7 @@ abstract contract AccountExecutor { // - (32 bytes) Sender // - (32 bytes) Value // - (32 bytes) data offset - // Totoal: 164 bytes + // Total: 164 bytes execCallBuffer := add(runtimeCallBuffer, 164) } } diff --git a/src/account/AccountStorageInitializable.sol b/src/account/AccountStorageInitializable.sol index 69de828c..fd66b269 100644 --- a/src/account/AccountStorageInitializable.sol +++ b/src/account/AccountStorageInitializable.sol @@ -8,7 +8,7 @@ import {AccountStorageV1} from "../libraries/AccountStorageV1.sol"; /// @title Account Storage Initializable /// @author Alchemy /// @notice This enables functions that can be called only once per implementation with the same storage layout -/// @dev Adapted from OpenZeppelin's Initialiazble and modified to use a diamond storage pattern. Removed +/// @dev Adapted from OpenZeppelin's Initializable and modified to use a diamond storage pattern. Removed /// Initialized() event since the account already emits an event on initialization. abstract contract AccountStorageInitializable is AccountStorageV1 { error AlreadyInitialized(); diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index fcf8f4af..ffe71598 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -320,7 +320,7 @@ contract UpgradeableModularAccount is // // 1. Check that the target is permitted at all, and if so check that any one of the following is true: // a. Is any selector permitted? - // b. Is the calldata is empty? (allow empty data calls by default if the target address is permitted) + // b. Is the calldata empty? (allow empty data calls by default if the target address is permitted) // c. Is the selector in the call permitted? // 2. If the target is not permitted, instead check whether all external calls are permitted. // @@ -345,7 +345,7 @@ contract UpgradeableModularAccount is revert ExecFromPluginExternalNotPermitted(callingPlugin, target, value, data); } - // Run permitted call hooks and execution hooks. `execfuteFromPluginExternal` doesn't use PermittedCallData + // Run permitted call hooks and execution hooks. `executeFromPluginExternal` doesn't use PermittedCallData // to check call permissions, nor do they have an address entry in SelectorData, so it doesn't make sense // to use cached booleans (hasPreExecHooks, hasPostOnlyExecHooks, etc.) to conditionally bypass certain // steps, as it would just be an added `sload` in the nonzero hooks case. @@ -743,7 +743,7 @@ contract UpgradeableModularAccount is } // "Trim" the associated post hook arrays to the actual length, since we may have overallocated. This - // allows for exeuction of post hooks in reverse order. + // allows for execution of post hooks in reverse order. assembly ("memory-safe") { mstore(postHooks, actualPostHooksToRunLength) mstore(postHookArgs, actualPostHooksToRunLength) diff --git a/src/interfaces/IPluginExecutor.sol b/src/interfaces/IPluginExecutor.sol index 07717c02..ca7e254a 100644 --- a/src/interfaces/IPluginExecutor.sol +++ b/src/interfaces/IPluginExecutor.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.21; /// @title Plugin Executor Interface interface IPluginExecutor { - /// @notice Method from cals made from plugins to other plugin execution functions. Plugins are not allowed to + /// @notice Method from calls made from plugins to other plugin execution functions. Plugins are not allowed to /// call accounts native functions. /// @dev Permissions must be granted to the calling plugin for the call to go through /// @param data The call data for the call. diff --git a/src/libraries/AccountStorageV1.sol b/src/libraries/AccountStorageV1.sol index 4bf00b63..cded2f8c 100644 --- a/src/libraries/AccountStorageV1.sol +++ b/src/libraries/AccountStorageV1.sol @@ -42,7 +42,7 @@ contract AccountStorageV1 { StoredInjectedHook[] injectedHooks; } - /// @dev A version of IPliginManager.InjectedHook used to track injected hooks in storage. Omits the + /// @dev A version of IPluginManager.InjectedHook used to track injected hooks in storage. Omits the /// hookApplyData field, which is not needed for storage, and flattens the struct. struct StoredInjectedHook { // The plugin that provides the hook diff --git a/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol b/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol index ce9c26cc..0bceee98 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol @@ -280,7 +280,7 @@ contract SessionKeyPermissionsPlugin is ISessionKeyPermissionsPlugin, SessionKey // Multiplier for the verification gas limit is 3 if there is a paymaster, 1 otherwise. // This is defined in EntryPoint v0.6.0, which uses the limit for the user op validation + paymaster // validation, then again for up to two more calls of `postOp`. Later versions of the EntryPoint may - // change this scale factor or the usage of the verificaiton gas limit, so this value should be checked + // change this scale factor or the usage of the verification gas limit, so this value should be checked // and updated if porting this plugin to a newer version of 4337. uint256 multiplier = userOp.paymasterAndData.length > 0 ? 3 : 1; uint256 maxGasFee = ( @@ -668,7 +668,7 @@ contract SessionKeyPermissionsPlugin is ISessionKeyPermissionsPlugin, SessionKey // previous amount by more than the spending limit, then back up to the previous amount plus the // spending limit. } - // Unrecognzied function selector + // Unrecognized function selector return 0; } From b14e0018dd30ac1d792602dd512adef969901f5c Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Tue, 12 Dec 2023 10:02:39 -0800 Subject: [PATCH 004/106] fix: Remove variable shadowing and lint deploy script (#13) --- script/Deploy.s.sol | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index 844b16cb..9413b1ae 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -83,8 +83,9 @@ contract Deploy is Script { // Deploy MultiOwnerMSCAFactory, and add stake with EP { if (ownerFactoryAddr == address(0)) { - ownerFactory = - new MultiOwnerMSCAFactory(owner, multiOwnerPlugin, mscaImpl, multiOwnerPluginManifestHash, entryPoint); + ownerFactory = new MultiOwnerMSCAFactory( + owner, multiOwnerPlugin, mscaImpl, multiOwnerPluginManifestHash, entryPoint + ); ownerFactoryAddr = address(ownerFactory); console.log("New MultiOwnerMSCAFactory: ", ownerFactoryAddr); @@ -96,8 +97,15 @@ contract Deploy is Script { // Deploy MultiOwnerTokenReceiverMSCAFactory, and add stake with EP if (ownerAndTokenReceiverFactoryAddr == address(0)) { - ownerAndTokenReceiverFactory = - new MultiOwnerTokenReceiverMSCAFactory(owner, multiOwnerPlugin, tokenReceiverPlugin, mscaImpl, multiOwnerPluginManifestHash, tokenReceiverPluginManifestHash, entryPoint); + ownerAndTokenReceiverFactory = new MultiOwnerTokenReceiverMSCAFactory( + owner, + multiOwnerPlugin, + tokenReceiverPlugin, + mscaImpl, + multiOwnerPluginManifestHash, + tokenReceiverPluginManifestHash, + entryPoint + ); ownerAndTokenReceiverFactoryAddr = address(ownerAndTokenReceiverFactory); console.log("New MultiOwnerTokenReceiverMSCAFactory: ", ownerAndTokenReceiverFactoryAddr); @@ -128,19 +136,19 @@ contract Deploy is Script { vm.stopBroadcast(); } - function _addStakeForFactory(address factoryAddr, IMSCAEntryPoint entryPoint) internal { + function _addStakeForFactory(address factoryAddr, IMSCAEntryPoint anEntryPoint) internal { uint32 unstakeDelaySec = uint32(vm.envOr("UNSTAKE_DELAY_SEC", uint32(60))); uint256 requiredStakeAmount = vm.envUint("REQUIRED_STAKE_AMOUNT") * 1 ether; - uint256 currentStakedAmount = IEntryPoint(address(entryPoint)).getDepositInfo(factoryAddr).stake; + uint256 currentStakedAmount = IEntryPoint(address(anEntryPoint)).getDepositInfo(factoryAddr).stake; uint256 stakeAmount = requiredStakeAmount - currentStakedAmount; // since all factory share the same addStake method, it does not matter which contract we use to cast the // address MultiOwnerMSCAFactory(payable(factoryAddr)).addStake{value: stakeAmount}(unstakeDelaySec, stakeAmount); console.log("******** Add Stake Verify *********"); console.log("Staked factory: ", factoryAddr); - console.log("Stake amount: ", IEntryPoint(address(entryPoint)).getDepositInfo(factoryAddr).stake); + console.log("Stake amount: ", IEntryPoint(address(anEntryPoint)).getDepositInfo(factoryAddr).stake); console.log( - "Unstake delay: ", IEntryPoint(address(entryPoint)).getDepositInfo(factoryAddr).unstakeDelaySec + "Unstake delay: ", IEntryPoint(address(anEntryPoint)).getDepositInfo(factoryAddr).unstakeDelaySec ); console.log("******** Stake Verify Done *********"); } From 7e58e9993255ebf3e238b9330502d77da96249e3 Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Tue, 12 Dec 2023 12:45:45 -0800 Subject: [PATCH 005/106] fix: [spearbit-76] Session key data storage collision (#14) --- .../permissions/SessionKeyPermissionsBase.sol | 10 +--- .../SessionKeyPermissionsPlugin.t.sol | 53 +++++++++++++++++++ 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/src/plugins/session/permissions/SessionKeyPermissionsBase.sol b/src/plugins/session/permissions/SessionKeyPermissionsBase.sol index d41a15f8..f9e95cbd 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsBase.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsBase.sol @@ -54,12 +54,6 @@ abstract contract SessionKeyPermissionsBase is ISessionKeyPermissionsPlugin { uint256 limitUsed; } - // PluginStorageLib KEY DEFINITIONS - // When adding a new permission type, you must: - // 1. Add new prefixes here for all stored structs - // 2. Define the key derivation and checking functions in a new file - // 3. Use the checking function in PermissionsCheckerPlugin - // Prefixes: bytes4 internal constant SESSION_KEY_ID_PREFIX = bytes4(0x1a01dae4); // bytes4(keccak256("SessionKeyId")) bytes4 internal constant SESSION_KEY_DATA_PREFIX = bytes4(0x16bff296); // bytes4(keccak256("SessionKeyData")) @@ -84,7 +78,7 @@ abstract contract SessionKeyPermissionsBase is ISessionKeyPermissionsPlugin { // ContractData (128 bytes) // 12 padding zeros || associated address || CONTRACT_DATA_PREFIX || batch index || sessionKeyId - // || 12 padding zero bytes || contractAddress + // || contractAddress || 12 padding zero bytes // FunctionData (128 bytes) // 12 padding zeros || associated address || FUNCTION_DATA_PREFIX || batch index || sessionKeyId || selector @@ -130,7 +124,7 @@ abstract contract SessionKeyPermissionsBase is ISessionKeyPermissionsPlugin { bytes memory associatedStorageKey = PluginStorageLib.allocateAssociatedStorageKey(associated, prefixAndBatchIndex, 1); - bytes32 sessionKeyDataKey = bytes32(abi.encodePacked(SESSION_KEY_DATA_PREFIX, SessionKeyId.unwrap(id))); + bytes32 sessionKeyDataKey = SessionKeyId.unwrap(id); return _toSessionKeyData(PluginStorageLib.associatedStorageLookup(associatedStorageKey, sessionKeyDataKey)); } diff --git a/test/plugin/session/permissions/SessionKeyPermissionsPlugin.t.sol b/test/plugin/session/permissions/SessionKeyPermissionsPlugin.t.sol index 6cc525ab..5833bab2 100644 --- a/test/plugin/session/permissions/SessionKeyPermissionsPlugin.t.sol +++ b/test/plugin/session/permissions/SessionKeyPermissionsPlugin.t.sol @@ -591,6 +591,59 @@ contract SessionKeyPermissionsPluginTest is Test { SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); } + function test_sessionKeyPerms_independentKeyStorage() public { + address sessionKey2 = makeAddr("sessionKey2"); + + address[] memory sessionKeysToAdd = new address[](1); + sessionKeysToAdd[0] = sessionKey2; + + vm.prank(owner1); + SessionKeyPlugin(address(account1)).updateSessionKeys( + sessionKeysToAdd, new SessionKeyPlugin.SessionKeyToRemove[](0) + ); + vm.prank(owner1); + SessionKeyPermissionsPlugin(address(account1)).registerKey(sessionKey2, 0); + + ISessionKeyPermissionsPlugin.ContractAccessControlType accessControlType1; + ISessionKeyPermissionsPlugin.ContractAccessControlType accessControlType2; + + accessControlType1 = sessionKeyPermissionsPlugin.getAccessControlType(address(account1), sessionKey1); + accessControlType2 = sessionKeyPermissionsPlugin.getAccessControlType(address(account1), sessionKey2); + + assertEq( + uint8(accessControlType1), + uint8(ISessionKeyPermissionsPlugin.ContractAccessControlType.ALLOWLIST), + "sessionKey1 should start with an allowlist" + ); + assertEq( + uint8(accessControlType2), + uint8(ISessionKeyPermissionsPlugin.ContractAccessControlType.ALLOWLIST), + "sessionKey2 should start with an allowlist" + ); + + bytes[] memory updates = new bytes[](1); + updates[0] = abi.encodeCall( + ISessionKeyPermissionsUpdates.setAccessListType, + (ISessionKeyPermissionsPlugin.ContractAccessControlType.NONE) + ); + vm.prank(owner1); + SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + + accessControlType1 = sessionKeyPermissionsPlugin.getAccessControlType(address(account1), sessionKey1); + accessControlType2 = sessionKeyPermissionsPlugin.getAccessControlType(address(account1), sessionKey2); + + assertEq( + uint8(accessControlType1), + uint8(ISessionKeyPermissionsPlugin.ContractAccessControlType.NONE), + "sessionKey1 should now have no allowlist" + ); + assertEq( + uint8(accessControlType2), + uint8(ISessionKeyPermissionsPlugin.ContractAccessControlType.ALLOWLIST), + "sessionKey2 should still have an allowlist" + ); + } + function _runSessionKeyExecUserOp( address target, address sessionKey, From 84f4b3dc8c3257bcccffa5788330a14ee8473b79 Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Tue, 12 Dec 2023 13:33:51 -0800 Subject: [PATCH 006/106] fix: [spearbit-38] ERC-1271 signature validation with EOA owners of contract owners of MSCA (#15) --- src/plugins/owner/MultiOwnerPlugin.sol | 37 ++++++++++----------- test/mocks/ContractOwner.sol | 16 ++++++++- test/plugin/owner/MultiOwnerPlugin.t.sol | 41 +++++++++++++++++++++++- 3 files changed, 72 insertions(+), 22 deletions(-) diff --git a/src/plugins/owner/MultiOwnerPlugin.sol b/src/plugins/owner/MultiOwnerPlugin.sol index c9404a58..47588c2f 100644 --- a/src/plugins/owner/MultiOwnerPlugin.sol +++ b/src/plugins/owner/MultiOwnerPlugin.sol @@ -136,18 +136,15 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271, EIP712 { function isValidSignature(bytes32 digest, bytes memory signature) public view override returns (bytes4) { bytes memory messageData = encodeMessageData(msg.sender, abi.encode(digest)); bytes32 messageHash = keccak256(messageData); + // try to recover through ECDSA (address signer, ECDSA.RecoverError error) = ECDSA.tryRecover(messageHash, signature); - if (error == ECDSA.RecoverError.NoError) { - if (_owners.contains(msg.sender, CastLib.toSetValue(signer))) { - return _1271_MAGIC_VALUE; - } else { - return _1271_MAGIC_VALUE_FAILURE; - } - } else { - if (_isValidERC1271OwnerTypeSignature(msg.sender, messageHash, signature)) { - return _1271_MAGIC_VALUE; - } + if (error == ECDSA.RecoverError.NoError && _owners.contains(msg.sender, CastLib.toSetValue(signer))) { + return _1271_MAGIC_VALUE; + } + + if (_isValidERC1271OwnerTypeSignature(msg.sender, messageHash, signature)) { + return _1271_MAGIC_VALUE; } return _1271_MAGIC_VALUE_FAILURE; @@ -208,20 +205,20 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271, EIP712 { override returns (uint256) { - (address signer, ECDSA.RecoverError error) = - (userOpHash.toEthSignedMessageHash()).tryRecover(userOp.signature); if (functionId == uint8(FunctionId.USER_OP_VALIDATION_OWNER)) { - if (error == ECDSA.RecoverError.NoError) { - if (isOwnerOf(msg.sender, signer)) { - return _SIG_VALIDATION_PASSED; - } - } else { - if (_isValidERC1271OwnerTypeSignature(msg.sender, userOpHash, userOp.signature)) { - return _SIG_VALIDATION_PASSED; - } + (address signer, ECDSA.RecoverError error) = + userOpHash.toEthSignedMessageHash().tryRecover(userOp.signature); + if (error == ECDSA.RecoverError.NoError && isOwnerOf(msg.sender, signer)) { + return _SIG_VALIDATION_PASSED; + } + + if (_isValidERC1271OwnerTypeSignature(msg.sender, userOpHash, userOp.signature)) { + return _SIG_VALIDATION_PASSED; } + return _SIG_VALIDATION_FAILED; } + revert NotImplemented(); } diff --git a/test/mocks/ContractOwner.sol b/test/mocks/ContractOwner.sol index f29530ac..5ed0c3f9 100644 --- a/test/mocks/ContractOwner.sol +++ b/test/mocks/ContractOwner.sol @@ -1,18 +1,32 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.21; +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; + import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol"; contract ContractOwner is IERC1271 { bytes4 internal constant _1271_MAGIC_VALUE = 0x1626ba7e; + address public owner; + + constructor(address _owner) { + owner = _owner; + } function sign(bytes32 digest) public pure returns (bytes memory) { return abi.encodePacked("Signed: ", digest); } - function isValidSignature(bytes32 digest, bytes memory signature) public pure override returns (bytes4) { + function isValidSignature(bytes32 digest, bytes memory signature) public view override returns (bytes4) { if (keccak256(signature) == keccak256(sign(digest))) { + // simple owner sig validation path return _1271_MAGIC_VALUE; + } else { + // EOA owner of this contractOwner path + (address signer,) = ECDSA.tryRecover(digest, signature); + if (signer == owner) { + return _1271_MAGIC_VALUE; + } } return 0xffffffff; } diff --git a/test/plugin/owner/MultiOwnerPlugin.t.sol b/test/plugin/owner/MultiOwnerPlugin.t.sol index e1d507de..12f9bbd0 100644 --- a/test/plugin/owner/MultiOwnerPlugin.t.sol +++ b/test/plugin/owner/MultiOwnerPlugin.t.sol @@ -29,6 +29,8 @@ contract MultiOwnerPluginTest is Test { address public owner1; address public owner2; address public owner3; + address public ownerofContractOwner; + uint256 public ownerofContractOwnerKey; ContractOwner public contractOwner; address[] public ownerArray; @@ -41,7 +43,8 @@ contract MultiOwnerPluginTest is Test { owner1 = makeAddr("owner1"); owner2 = makeAddr("owner2"); owner3 = makeAddr("owner3"); - contractOwner = new ContractOwner(); + (ownerofContractOwner, ownerofContractOwnerKey) = makeAddrAndKey("ownerofContractOwner"); + contractOwner = new ContractOwner(ownerofContractOwner); // set up owners for accountA ownerArray = new address[](3); @@ -172,6 +175,18 @@ contract MultiOwnerPluginTest is Test { assertEq(_1271_MAGIC_VALUE, plugin.isValidSignature(digest, signature)); } + function testFuzz_isValidSignature_ContractOwnerWithEOAOwner(bytes32 digest) public { + address[] memory ownersToAdd = new address[](1); + ownersToAdd[0] = address(contractOwner); + plugin.updateOwners(ownersToAdd, new address[](0)); + + bytes32 messageDigest = plugin.getMessageHash(address(accountA), abi.encode(digest)); + // owner3 is the EOA Owner of the contractOwner + (uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerofContractOwnerKey, messageDigest); + bytes memory signature = abi.encodePacked(r, s, v); + assertEq(_1271_MAGIC_VALUE, plugin.isValidSignature(digest, signature)); + } + function test_runtimeValidationFunction_OwnerOrSelf() public { // should pass with owner as sender plugin.runtimeValidationFunction( @@ -210,6 +225,30 @@ contract MultiOwnerPluginTest is Test { assertEq(resSuccess, 0); } + function testFuzz_userOpValidationFunction_ContractOwnerWithEOAOwner(UserOperation memory userOp) public { + bytes32 userOpHash = entryPoint.getUserOpHash(userOp); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerofContractOwnerKey, userOpHash); + + // sig cannot cover the whole userop struct since userop struct has sig field + userOp.signature = abi.encodePacked(r, s, v); + + // should fail without owner access + uint256 resFail = plugin.userOpValidationFunction( + uint8(IMultiOwnerPlugin.FunctionId.USER_OP_VALIDATION_OWNER), userOp, userOpHash + ); + assertEq(resFail, 1); + + address[] memory ownersToAdd = new address[](1); + ownersToAdd[0] = address(contractOwner); + plugin.updateOwners(ownersToAdd, new address[](0)); + + // should pass with owner access + uint256 resSuccess = plugin.userOpValidationFunction( + uint8(IMultiOwnerPlugin.FunctionId.USER_OP_VALIDATION_OWNER), userOp, userOpHash + ); + assertEq(resSuccess, 0); + } + function testFuzz_userOpValidationFunction_EOAOwner(string memory salt, UserOperation memory userOp) public { // range bound the possible set of priv keys (address signer, uint256 privateKey) = makeAddrAndKey(salt); From f70c9e589d56f42c06b322ab7ba7b33f7423af85 Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Wed, 13 Dec 2023 11:43:11 -0800 Subject: [PATCH 007/106] fix: [spearbit-15] IPlugin function prevention in installing execution functions (#17) --- src/account/PluginManagerInternals.sol | 9 +++++++- src/helpers/KnownSelectors.sol | 12 ++++++++++ ...gradeableModularAccountPluginManager.t.sol | 23 +++++++++++++++++++ test/helpers/KnownSelectors.t.sol | 20 +++++++++++++++- 4 files changed, 62 insertions(+), 2 deletions(-) diff --git a/src/account/PluginManagerInternals.sol b/src/account/PluginManagerInternals.sol index aa125703..fc6104e1 100644 --- a/src/account/PluginManagerInternals.sol +++ b/src/account/PluginManagerInternals.sol @@ -50,6 +50,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { error Erc4337FunctionNotAllowed(bytes4 selector); error ExecutionFunctionAlreadySet(bytes4 selector); error ExecutionFunctionNotSet(bytes4 selector); + error IPluginFunctionNotAllowed(bytes4 selector); error InvalidDependenciesProvided(); error InvalidPluginManifest(); error MissingPluginDependency(address dependency); @@ -75,11 +76,17 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { revert ExecutionFunctionAlreadySet(selector); } - // make sure incoming execution function does not collide with any native functions (data are stored on the + // Make sure incoming execution function does not collide with any native functions (data are stored on the // account implementation contract) if (KnownSelectors.isNativeFunction(selector)) { revert NativeFunctionNotAllowed(selector); } + + // Make sure incoming execution function is not a function in IPlugin + if (KnownSelectors.isIPluginFunction(selector)) { + revert IPluginFunctionNotAllowed(selector); + } + // Also make sure it doesn't collide with functions defined by ERC-4337 // and called by the entry point. This prevents a malicious plugin from // sneaking in a function with the same selector as e.g. diff --git a/src/helpers/KnownSelectors.sol b/src/helpers/KnownSelectors.sol index 3395785d..bb573c7a 100644 --- a/src/helpers/KnownSelectors.sol +++ b/src/helpers/KnownSelectors.sol @@ -10,6 +10,7 @@ import {IAccountLoupe} from "../../src/interfaces/IAccountLoupe.sol"; import {IAccountView} from "../../src/interfaces/IAccountView.sol"; import {IAggregator} from "../../src/interfaces/erc4337/IAggregator.sol"; import {IPaymaster} from "../../src/interfaces/erc4337/IPaymaster.sol"; +import {IPlugin} from "../interfaces/IPlugin.sol"; import {IPluginExecutor} from "../interfaces/IPluginExecutor.sol"; import {IPluginManager} from "../../src/interfaces/IPluginManager.sol"; import {IStandardExecutor} from "../interfaces/IStandardExecutor.sol"; @@ -53,4 +54,15 @@ library KnownSelectors { || selector == IAggregator.aggregateSignatures.selector || selector == IPaymaster.validatePaymasterUserOp.selector || selector == IPaymaster.postOp.selector; } + + function isIPluginFunction(bytes4 selector) internal pure returns (bool) { + return selector == IPlugin.onInstall.selector || selector == IPlugin.onUninstall.selector + || selector == IPlugin.preUserOpValidationHook.selector + || selector == IPlugin.userOpValidationFunction.selector + || selector == IPlugin.preRuntimeValidationHook.selector + || selector == IPlugin.runtimeValidationFunction.selector || selector == IPlugin.preExecutionHook.selector + || selector == IPlugin.postExecutionHook.selector || selector == IPlugin.onHookApply.selector + || selector == IPlugin.onHookUnapply.selector || selector == IPlugin.pluginManifest.selector + || selector == IPlugin.pluginMetadata.selector; + } } diff --git a/test/account/UpgradeableModularAccountPluginManager.t.sol b/test/account/UpgradeableModularAccountPluginManager.t.sol index 95eacb5f..70b65a9d 100644 --- a/test/account/UpgradeableModularAccountPluginManager.t.sol +++ b/test/account/UpgradeableModularAccountPluginManager.t.sol @@ -263,6 +263,29 @@ contract UpgradeableModularAccountPluginManagerTest is Test { }); } + function test_installPlugin_failWithIPluginFunctionSelector() public { + vm.startPrank(owner2); + + PluginManifest memory manifestBad; + manifestBad.executionFunctions = new bytes4[](1); + manifestBad.executionFunctions[0] = IPlugin.onInstall.selector; + MockPlugin mockPluginBad = new MockPlugin(manifestBad); + bytes32 manifestHashBad = keccak256(abi.encode(mockPluginBad.pluginManifest())); + + vm.expectRevert( + abi.encodeWithSelector( + PluginManagerInternals.IPluginFunctionNotAllowed.selector, IPlugin.onInstall.selector + ) + ); + IPluginManager(account2).installPlugin({ + plugin: address(mockPluginBad), + manifestHash: manifestHashBad, + pluginInitData: bytes(""), + dependencies: new FunctionReference[](0), + injectedHooks: new IPluginManager.InjectedHook[](0) + }); + } + function test_installPlugin_failWtihErc4337FunctionSelector() public { vm.startPrank(owner2); diff --git a/test/helpers/KnownSelectors.t.sol b/test/helpers/KnownSelectors.t.sol index 2f122eb4..6fe29662 100644 --- a/test/helpers/KnownSelectors.t.sol +++ b/test/helpers/KnownSelectors.t.sol @@ -12,9 +12,10 @@ import {IPaymaster} from "@eth-infinitism/account-abstraction/interfaces/IPaymas import {KnownSelectors} from "../../src/helpers/KnownSelectors.sol"; import {IAccountLoupe} from "../../src/interfaces/IAccountLoupe.sol"; import {IAccountInitializable} from "../../src/interfaces/IAccountInitializable.sol"; -import {IStandardExecutor} from "../../src/interfaces/IStandardExecutor.sol"; +import {IPlugin} from "../../src/interfaces/IPlugin.sol"; import {IPluginExecutor} from "../../src/interfaces/IPluginExecutor.sol"; import {IPluginManager} from "../../src/interfaces/IPluginManager.sol"; +import {IStandardExecutor} from "../../src/interfaces/IStandardExecutor.sol"; contract KnownSelectorsTest is Test { function test_isNativeFunction() public { @@ -64,4 +65,21 @@ contract KnownSelectorsTest is Test { assertFalse(KnownSelectors.isErc4337Function(BaseAccount.validateUserOp.selector)); } + + function test_isIPluginFunction() public { + assertTrue(KnownSelectors.isIPluginFunction(IPlugin.onInstall.selector)); + assertTrue(KnownSelectors.isIPluginFunction(IPlugin.onUninstall.selector)); + assertTrue(KnownSelectors.isIPluginFunction(IPlugin.preUserOpValidationHook.selector)); + assertTrue(KnownSelectors.isIPluginFunction(IPlugin.userOpValidationFunction.selector)); + assertTrue(KnownSelectors.isIPluginFunction(IPlugin.preRuntimeValidationHook.selector)); + assertTrue(KnownSelectors.isIPluginFunction(IPlugin.runtimeValidationFunction.selector)); + assertTrue(KnownSelectors.isIPluginFunction(IPlugin.preExecutionHook.selector)); + assertTrue(KnownSelectors.isIPluginFunction(IPlugin.postExecutionHook.selector)); + assertTrue(KnownSelectors.isIPluginFunction(IPlugin.onHookApply.selector)); + assertTrue(KnownSelectors.isIPluginFunction(IPlugin.onHookUnapply.selector)); + assertTrue(KnownSelectors.isIPluginFunction(IPlugin.pluginManifest.selector)); + assertTrue(KnownSelectors.isIPluginFunction(IPlugin.pluginMetadata.selector)); + + assertFalse(KnownSelectors.isIPluginFunction(IPaymaster.postOp.selector)); + } } From bfb9b652ffd94d53b7dc5bfc406ed615e11aa284 Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Wed, 13 Dec 2023 12:26:51 -0800 Subject: [PATCH 008/106] fix: [spearbit-34] Session key rotation registration check (#19) --- .../SessionKeyPermissionsPlugin.sol | 7 +++++ .../SessionKeyPermissionsPlugin.t.sol | 29 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol b/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol index 0bceee98..b9435799 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol @@ -55,8 +55,15 @@ contract SessionKeyPermissionsPlugin is ISessionKeyPermissionsPlugin, SessionKey /// @inheritdoc ISessionKeyPermissionsPlugin function rotateKey(address oldSessionKey, address newSessionKey) external override { + // Assert that the new key is not already registered + SessionKeyId newKeyId = _sessionKeyIdOf(msg.sender, newSessionKey); + if (SessionKeyId.unwrap(newKeyId) != bytes32(0)) { + revert KeyAlreadyRegistered(newSessionKey); + } + // Assert that the old key is registered SessionKeyId keyId = _sessionKeyIdOf(msg.sender, oldSessionKey); _assertRegistered(keyId, oldSessionKey); + // Update the key ID of the old key to zero, and the new key to the old key's ID _updateSessionKeyId(msg.sender, oldSessionKey, SessionKeyId.wrap(bytes32(0))); _updateSessionKeyId(msg.sender, newSessionKey, keyId); diff --git a/test/plugin/session/permissions/SessionKeyPermissionsPlugin.t.sol b/test/plugin/session/permissions/SessionKeyPermissionsPlugin.t.sol index 5833bab2..33ce259d 100644 --- a/test/plugin/session/permissions/SessionKeyPermissionsPlugin.t.sol +++ b/test/plugin/session/permissions/SessionKeyPermissionsPlugin.t.sol @@ -448,6 +448,35 @@ contract SessionKeyPermissionsPluginTest is Test { assertEq(returnedEndTime, endTime); } + function test_rotateKey_invalid_alreadyRegistered() public { + // Attempt to rotate the key to itself + vm.expectRevert( + abi.encodeWithSelector(ISessionKeyPermissionsPlugin.KeyAlreadyRegistered.selector, sessionKey1) + ); + vm.prank(owner1); + SessionKeyPermissionsPlugin(address(account1)).rotateKey(sessionKey1, sessionKey1); + + // Add a second session key + address sessionKey2 = makeAddr("sessionKey2"); + address[] memory keysToAdd = new address[](1); + keysToAdd[0] = sessionKey2; + vm.prank(owner1); + SessionKeyPlugin(address(account1)).updateSessionKeys( + keysToAdd, new SessionKeyPlugin.SessionKeyToRemove[](0) + ); + + // Register the second session key + vm.prank(owner1); + SessionKeyPermissionsPlugin(address(account1)).registerKey(sessionKey2, 0); + + // Attempt to rotate the key to the second session key + vm.expectRevert( + abi.encodeWithSelector(ISessionKeyPermissionsPlugin.KeyAlreadyRegistered.selector, sessionKey2) + ); + vm.prank(owner1); + SessionKeyPermissionsPlugin(address(account1)).rotateKey(sessionKey1, sessionKey2); + } + function testFuzz_sessionKeyPermissions_setRequiredPaymaster(address requiredPaymaster) public { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setRequiredPaymaster, (requiredPaymaster)); From 9d2fc290af39f441d2bc836b8fef2f45053752a0 Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Thu, 14 Dec 2023 15:04:35 -0800 Subject: [PATCH 009/106] fix: Reorder CI elements to run `forge fmt` on the correct code (#21) --- .github/workflows/test.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2d39989c..a45ada40 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,9 +17,6 @@ jobs: uses: foundry-rs/foundry-toolchain@v1 with: version: nightly-5be158ba6dc7c798a6f032026fe60fc01686b33b - - run: forge install - - - run: forge fmt --check - name: "Check out the repo" uses: "actions/checkout@v3" @@ -43,6 +40,10 @@ jobs: - name: "Install the Node.js dependencies" run: "pnpm install" + - run: forge install + + - run: forge fmt --check + - name: "Lint the contracts" run: "pnpm lint" From ef789cdb3fae133390d0464b36861fbae6cdd8b8 Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Mon, 18 Dec 2023 14:43:34 -0800 Subject: [PATCH 010/106] fix: [spearbit-94] runtime validation bypass special case should only allow installPlugin and upgradeToAndCall (#22) --- src/account/UpgradeableModularAccount.sol | 4 +-- ...gradeableModularAccountPluginManager.t.sol | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index ffe71598..1fd6e3c2 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -619,8 +619,8 @@ contract UpgradeableModularAccount is && ( ( msg.sig != IPluginManager.installPlugin.selector - || msg.sig != UUPSUpgradeable.upgradeToAndCall.selector - ) && msg.sender != address(this) + && msg.sig != UUPSUpgradeable.upgradeToAndCall.selector + ) || msg.sender != address(this) ) ) { // Runtime calls cannot be made against functions with no diff --git a/test/account/UpgradeableModularAccountPluginManager.t.sol b/test/account/UpgradeableModularAccountPluginManager.t.sol index 70b65a9d..c848b6ff 100644 --- a/test/account/UpgradeableModularAccountPluginManager.t.sol +++ b/test/account/UpgradeableModularAccountPluginManager.t.sol @@ -858,6 +858,36 @@ contract UpgradeableModularAccountPluginManagerTest is Test { IStandardExecutor(account2).executeBatch(calls); } + function test_uninstallAndInstallInBatch_failwithOtherCalls() external { + // Test fail case for a special use case in `installPlugin`: + // We can uninstall the `MultiOwnerPlugin`, leaving no validator on `installPlugin`, and then install a + // different plugin immediately after as part of the same batch execution. This is a special case: normally + // an execution function with no runtime validator cannot be runtime-called. + // Here we test only the above is allowed, any other self-call is blocked + + vm.startPrank(owner2); + + Call[] memory calls = new Call[](2); + calls[0] = Call({ + target: address(account2), + value: 0, + data: abi.encodeCall(IPluginManager.uninstallPlugin, (address(multiOwnerPlugin), "", "", new bytes[](0))) + }); + calls[1] = Call({ + target: address(account2), + value: 0, + data: abi.encodeCall(UpgradeableModularAccount.execute, (ethRecipient, 1 wei, "")) + }); + + vm.expectRevert( + abi.encodeWithSelector( + UpgradeableModularAccount.RuntimeValidationFunctionMissing.selector, + UpgradeableModularAccount.execute.selector + ) + ); + IStandardExecutor(account2).executeBatch(calls); + } + function test_noNonSelfInstallAfterUninstall() external { // A companion to the previous test, ensuring that `installPlugin` can't // be called directly (e.g. not via `execute` or `executeBatch`) if it From 10330fe1f4721566eb758590a54ab7a0240d4618 Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Thu, 21 Dec 2023 15:13:29 -0800 Subject: [PATCH 011/106] fix: [quantstamp-20] clear plugin data early in uninstallPlugin (#25) and clean github lint action --- .github/workflows/test.yml | 24 +++++++++------------ src/account/PluginManagerInternals.sol | 30 +++++++++++++------------- 2 files changed, 25 insertions(+), 29 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a45ada40..68325d4d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,19 +12,15 @@ jobs: name: Run Linters runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Install Foundry - uses: foundry-rs/foundry-toolchain@v1 - with: - version: nightly-5be158ba6dc7c798a6f032026fe60fc01686b33b - - name: "Check out the repo" uses: "actions/checkout@v3" with: submodules: "recursive" - - name: "Install Foundry" - uses: "foundry-rs/foundry-toolchain@v1" + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly-5be158ba6dc7c798a6f032026fe60fc01686b33b - name: "Install Pnpm" uses: "pnpm/action-setup@v2" @@ -37,16 +33,14 @@ jobs: cache: "pnpm" node-version: "lts/*" - - name: "Install the Node.js dependencies" + - name: "Install Node.js dependencies" run: "pnpm install" - - run: forge install - - run: forge fmt --check - name: "Lint the contracts" run: "pnpm lint" - + # check-inspect: # name: Verify Inspections # runs-on: ubuntu-latest @@ -70,7 +64,8 @@ jobs: name: Run Forge Tests runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - name: "Check out the repo" + uses: actions/checkout@v3 with: submodules: recursive @@ -92,7 +87,8 @@ jobs: name: Run Forge Tests [lite build] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - name: "Check out the repo" + uses: actions/checkout@v3 with: submodules: recursive diff --git a/src/account/PluginManagerInternals.sol b/src/account/PluginManagerInternals.sol index fc6104e1..59421160 100644 --- a/src/account/PluginManagerInternals.sol +++ b/src/account/PluginManagerInternals.sol @@ -651,18 +651,20 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { revert PluginNotInstalled(args.plugin); } + PluginData memory pluginData = storage_.pluginData[args.plugin]; + // Check manifest hash. - if (!_isValidPluginManifest(args.manifest, storage_.pluginData[args.plugin].manifestHash)) { + if (!_isValidPluginManifest(args.manifest, pluginData.manifestHash)) { revert InvalidPluginManifest(); } // Ensure that there are no dependent plugins. - if (storage_.pluginData[args.plugin].dependentCount != 0) { + if (pluginData.dependentCount != 0) { revert PluginDependencyViolation(args.plugin); } // Remove this plugin as a dependent from its dependencies. - FunctionReference[] memory dependencies = storage_.pluginData[args.plugin].dependencies; + FunctionReference[] memory dependencies = pluginData.dependencies; uint256 length = dependencies.length; for (uint256 i = 0; i < length;) { FunctionReference dependency = dependencies[i]; @@ -676,6 +678,9 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { } } + // Remove the plugin metadata from the account. + delete storage_.pluginData[args.plugin]; + // Remove components according to the manifest, in reverse order (by component type) of their installation. // Remove pre and post permitted call hooks @@ -786,11 +791,9 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { } } - // Remove permitted external call permissions - if (args.manifest.permitAnyExternalAddress) { - // Only clear if it was set during install time - storage_.pluginData[args.plugin].anyExternalAddressPermitted = false; - } else { + // Remove permitted external call permissions, anyExternalAddressPermitted is cleared when pluginData being + // deleted + if (!args.manifest.permitAnyExternalAddress) { // Only clear the specific permitted external calls if "permit any" flag was not set. length = args.manifest.permittedExternalCalls.length; for (uint256 i = 0; i < length;) { @@ -823,9 +826,9 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { } // Remove injected hooks - length = storage_.pluginData[args.plugin].injectedHooks.length; + length = pluginData.injectedHooks.length; for (uint256 i = 0; i < length;) { - StoredInjectedHook memory hook = storage_.pluginData[args.plugin].injectedHooks[i]; + StoredInjectedHook memory hook = pluginData.injectedHooks[i]; // Decrement the dependent count for the plugin providing the hook. storage_.pluginData[hook.providingPlugin].dependentCount -= 1; @@ -877,13 +880,13 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { // Call onHookUnapply on all injected hooks bool callbacksSucceeded = true; - length = storage_.pluginData[args.plugin].injectedHooks.length; + length = pluginData.injectedHooks.length; bool hasUnapplyHookData = hookUnapplyData.length != 0; if (hasUnapplyHookData && hookUnapplyData.length != length) { revert ArrayLengthMismatch(); } for (uint256 i = 0; i < length;) { - StoredInjectedHook memory hook = storage_.pluginData[args.plugin].injectedHooks[i]; + StoredInjectedHook memory hook = pluginData.injectedHooks[i]; /* solhint-disable no-empty-blocks */ try IPlugin(hook.providingPlugin).onHookUnapply{gas: args.callbackGasLimit}( @@ -908,9 +911,6 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { } } - // Remove the plugin metadata from the account. - delete storage_.pluginData[args.plugin]; - // Clear the plugin storage for the account. // solhint-disable-next-line no-empty-blocks try IPlugin(args.plugin).onUninstall{gas: args.callbackGasLimit}(uninstallData) {} From 0619de599d98a805feb746346777d5ef1b0095fd Mon Sep 17 00:00:00 2001 From: howy <132113803+howydev@users.noreply.github.com> Date: Thu, 21 Dec 2023 19:57:32 -0500 Subject: [PATCH 012/106] fix: [spearbit-97] Prevent createAccount() user error (#26) --- src/factory/MultiOwnerMSCAFactory.sol | 31 +++++++++++++++++++ .../MultiOwnerTokenReceiverMSCAFactory.sol | 31 +++++++++++++++++++ test/factory/MultiOwnerMSCAFactoryTest.t.sol | 16 ++++++++++ 3 files changed, 78 insertions(+) diff --git a/src/factory/MultiOwnerMSCAFactory.sol b/src/factory/MultiOwnerMSCAFactory.sol index 1429124c..a8cfaad0 100644 --- a/src/factory/MultiOwnerMSCAFactory.sol +++ b/src/factory/MultiOwnerMSCAFactory.sol @@ -21,6 +21,10 @@ contract MultiOwnerMSCAFactory is Ownable { bytes32 internal immutable _MULTI_OWNER_PLUGIN_MANIFEST_HASH; IEntryPoint public immutable ENTRYPOINT; + error OwnersArrayEmpty(); + error ZeroAddressOwner(); + error DuplicateOwner(); + /// @notice Constructor for the factory constructor( address owner, @@ -41,6 +45,7 @@ contract MultiOwnerMSCAFactory is Ownable { /// @notice Create a modular smart contract account /// @dev Account address depends on salt, impl addr, plugins and plugin init data + /// @dev Owner array must be valid else the plugin installation step would revert. See getAddress below /// @param salt salt for additional entropy for create2 /// @param owners address array of the owners function createAccount(uint256 salt, address[] calldata owners) external returns (address addr) { @@ -102,9 +107,35 @@ contract MultiOwnerMSCAFactory is Ownable { } /// @notice Getter for counterfactual address based on input params + /// @dev Owner array cannot be empty, cannot contain address(0), and cannot contain duplicates /// @param salt salt for additional entropy for create2 /// @param owners array of addresses of the owner function getAddress(uint256 salt, address[] calldata owners) external view returns (address) { + // array can't be empty + if (owners.length == 0) { + revert OwnersArrayEmpty(); + } + + for (uint256 i = 0; i < owners.length;) { + // array can't contain address(0) + if (owners[i] == address(0)) { + revert ZeroAddressOwner(); + } + + for (uint256 j = i + 1; j < owners.length;) { + // array can't have matching elements + if (owners[i] == owners[j]) { + revert DuplicateOwner(); + } + unchecked { + ++j; + } + } + unchecked { + ++i; + } + } + return Create2.computeAddress( _getSalt(salt, abi.encode(owners)), keccak256(abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(IMPL, ""))) diff --git a/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol b/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol index 2461b6a8..2c42365b 100644 --- a/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol +++ b/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol @@ -23,6 +23,10 @@ contract MultiOwnerTokenReceiverMSCAFactory is Ownable { bytes32 internal immutable _TOKEN_RECEIVER_PLUGIN_MANIFEST_HASH; IEntryPoint public immutable ENTRYPOINT; + error OwnersArrayEmpty(); + error ZeroAddressOwner(); + error DuplicateOwner(); + /// @notice Constructor for the factory constructor( address owner, @@ -47,6 +51,7 @@ contract MultiOwnerTokenReceiverMSCAFactory is Ownable { /// @notice Create a modular smart contract account /// @dev Account address depends on salt, impl addr, plugins and plugin init data + /// @dev Owner array must be valid. See getAddress below /// @param salt salt for additional entropy for create2 /// @param owners address array of the owners function createAccount(uint256 salt, address[] calldata owners) external returns (address addr) { @@ -110,9 +115,35 @@ contract MultiOwnerTokenReceiverMSCAFactory is Ownable { } /// @notice Getter for counterfactual address based on input params + /// @dev Owner array cannot be empty, cannot contain address(0), and cannot contain duplicates /// @param salt salt for additional entropy for create2 /// @param owners array of addresses of the owner function getAddress(uint256 salt, address[] calldata owners) external view returns (address) { + // array can't be empty + if (owners.length == 0) { + revert OwnersArrayEmpty(); + } + + for (uint256 i = 0; i < owners.length;) { + // array can't contain address(0) + if (owners[i] == address(0)) { + revert ZeroAddressOwner(); + } + + for (uint256 j = i + 1; j < owners.length;) { + // array can't have matching elements + if (owners[i] == owners[j]) { + revert DuplicateOwner(); + } + unchecked { + ++j; + } + } + unchecked { + ++i; + } + } + return Create2.computeAddress( _getSalt(salt, abi.encode(owners)), keccak256(abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(IMPL, ""))) diff --git a/test/factory/MultiOwnerMSCAFactoryTest.t.sol b/test/factory/MultiOwnerMSCAFactoryTest.t.sol index 62aff26a..6b4e2101 100644 --- a/test/factory/MultiOwnerMSCAFactoryTest.t.sol +++ b/test/factory/MultiOwnerMSCAFactoryTest.t.sol @@ -83,6 +83,22 @@ contract MultiOwnerMSCAFactoryTest is Test { assertEq(plugins[0], address(multiOwnerPlugin)); } + function test_badOwnersArray() public { + vm.expectRevert(MultiOwnerMSCAFactory.OwnersArrayEmpty.selector); + factory.getAddress(0, new address[](0)); + + address[] memory badOwners = new address[](2); + + vm.expectRevert(MultiOwnerMSCAFactory.ZeroAddressOwner.selector); + factory.getAddress(0, badOwners); + + badOwners[0] = address(1); + badOwners[1] = address(1); + + vm.expectRevert(MultiOwnerMSCAFactory.DuplicateOwner.selector); + factory.getAddress(0, badOwners); + } + function test_addStake() public { assertEq(entryPoint.balanceOf(address(factory)), 0); vm.deal(address(this), 100 ether); From 4d4f76a24242697ff4b4ebfa0f58347da7032224 Mon Sep 17 00:00:00 2001 From: howy <132113803+howydev@users.noreply.github.com> Date: Fri, 22 Dec 2023 15:44:46 -0500 Subject: [PATCH 013/106] fix: [spearbit-45] 2 step ownable factories (#23) --- src/factory/MultiOwnerMSCAFactory.sol | 4 ++-- src/factory/MultiOwnerTokenReceiverMSCAFactory.sol | 4 ++-- test/factory/MultiOwnerMSCAFactoryTest.t.sol | 9 +++++++++ test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol | 9 +++++++++ 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/factory/MultiOwnerMSCAFactory.sol b/src/factory/MultiOwnerMSCAFactory.sol index a8cfaad0..6e63337f 100644 --- a/src/factory/MultiOwnerMSCAFactory.sol +++ b/src/factory/MultiOwnerMSCAFactory.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.21; import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {IAccountInitializable} from "../interfaces/IAccountInitializable.sol"; @@ -15,7 +15,7 @@ import {IEntryPoint} from "../interfaces/erc4337/IEntryPoint.sol"; /// @notice Factory for upgradeable modular accounts with MultiOwnerPlugin installed. /// @dev There is a reliance on the assumption that the plugin manifest will remain static, following ERC-6900. If /// this assumption is broken then account deployments would be bricked. -contract MultiOwnerMSCAFactory is Ownable { +contract MultiOwnerMSCAFactory is Ownable2Step { address public immutable MULTI_OWNER_PLUGIN; address public immutable IMPL; bytes32 internal immutable _MULTI_OWNER_PLUGIN_MANIFEST_HASH; diff --git a/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol b/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol index 2c42365b..9fe0eb02 100644 --- a/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol +++ b/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.21; import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {IAccountInitializable} from "../interfaces/IAccountInitializable.sol"; @@ -15,7 +15,7 @@ import {IEntryPoint} from "../interfaces/erc4337/IEntryPoint.sol"; /// @notice Factory for upgradeable modular accounts with MultiOwnerPlugin and TokenReceiver installed. /// @dev There is a reliance on the assumption that the plugin manifest will remain static, following ERC-6900. If /// this assumption is broken then account deployments would be bricked. -contract MultiOwnerTokenReceiverMSCAFactory is Ownable { +contract MultiOwnerTokenReceiverMSCAFactory is Ownable2Step { address public immutable MULTI_OWNER_PLUGIN; address public immutable TOKEN_RECEIVER_PLUGIN; address public immutable IMPL; diff --git a/test/factory/MultiOwnerMSCAFactoryTest.t.sol b/test/factory/MultiOwnerMSCAFactoryTest.t.sol index 6b4e2101..67be8a50 100644 --- a/test/factory/MultiOwnerMSCAFactoryTest.t.sol +++ b/test/factory/MultiOwnerMSCAFactoryTest.t.sol @@ -130,6 +130,15 @@ contract MultiOwnerMSCAFactoryTest is Test { assertEq(address(factory).balance, 0); } + function test_2StepOwnershipTransfer() public { + assertEq(factory.owner(), address(this)); + factory.transferOwnership(owner1); + assertEq(factory.owner(), address(this)); + vm.prank(owner1); + factory.acceptOwnership(); + assertEq(factory.owner(), owner1); + } + // to receive funds from withdraw receive() external payable {} } diff --git a/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol b/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol index 3d5116ff..a42c4343 100644 --- a/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol +++ b/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol @@ -186,6 +186,15 @@ contract MultiOwnerTokenReceiverMSCAFactoryTest is Test { assertEq(address(factory).balance, 0); } + function test_2StepOwnershipTransfer() public { + assertEq(factory.owner(), address(this)); + factory.transferOwnership(owner1); + assertEq(factory.owner(), address(this)); + vm.prank(owner1); + factory.acceptOwnership(); + assertEq(factory.owner(), owner1); + } + // to receive funds from withdraw receive() external payable {} } From fd097ef7a179e296c3301c61304220ce2705d74e Mon Sep 17 00:00:00 2001 From: howy <132113803+howydev@users.noreply.github.com> Date: Fri, 22 Dec 2023 15:44:58 -0500 Subject: [PATCH 014/106] fix: [spearbit-55] switch from .transfer to .call (#24) --- src/factory/MultiOwnerMSCAFactory.sol | 6 +++++- src/factory/MultiOwnerTokenReceiverMSCAFactory.sol | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/factory/MultiOwnerMSCAFactory.sol b/src/factory/MultiOwnerMSCAFactory.sol index 6e63337f..e4e7a7e6 100644 --- a/src/factory/MultiOwnerMSCAFactory.sol +++ b/src/factory/MultiOwnerMSCAFactory.sol @@ -24,6 +24,7 @@ contract MultiOwnerMSCAFactory is Ownable2Step { error OwnersArrayEmpty(); error ZeroAddressOwner(); error DuplicateOwner(); + error TransferFailed(); /// @notice Constructor for the factory constructor( @@ -100,7 +101,10 @@ contract MultiOwnerMSCAFactory is Ownable2Step { /// @param amount amount of the token to withdraw in case of rebasing tokens function withdraw(address payable to, address token, uint256 amount) external onlyOwner { if (token == address(0)) { - to.transfer(address(this).balance); + (bool success,) = to.call{value: address(this).balance}(""); + if (!success) { + revert TransferFailed(); + } } else { SafeERC20.safeTransfer(IERC20(token), to, amount); } diff --git a/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol b/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol index 9fe0eb02..7cd6fa14 100644 --- a/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol +++ b/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol @@ -26,6 +26,7 @@ contract MultiOwnerTokenReceiverMSCAFactory is Ownable2Step { error OwnersArrayEmpty(); error ZeroAddressOwner(); error DuplicateOwner(); + error TransferFailed(); /// @notice Constructor for the factory constructor( @@ -108,7 +109,10 @@ contract MultiOwnerTokenReceiverMSCAFactory is Ownable2Step { /// @param amount amount of the token to withdraw in case of rebasing tokens function withdraw(address payable to, address token, uint256 amount) external onlyOwner { if (token == address(0)) { - to.transfer(address(this).balance); + (bool success,) = to.call{value: address(this).balance}(""); + if (!success) { + revert TransferFailed(); + } } else { SafeERC20.safeTransfer(IERC20(token), to, amount); } From 807823b260e5617f81c6784aee639a2fcce549b9 Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Tue, 2 Jan 2024 10:47:14 -0800 Subject: [PATCH 015/106] fix: [quantstamp-3] add target is not self check in executeFromPluginExternal (#29) --- src/account/UpgradeableModularAccount.sol | 5 +++ .../ExecuteFromPluginPermissions.t.sol | 36 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index 1fd6e3c2..d1ab7cc5 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -316,6 +316,11 @@ contract UpgradeableModularAccount is revert NativeTokenSpendingNotPermitted(callingPlugin); } + // Target cannot be the account itself. + if (target == address(this)) { + revert ExecFromPluginExternalNotPermitted(callingPlugin, target, value, data); + } + // Check the caller plugin's permission to make this call on the target address. // // 1. Check that the target is permitted at all, and if so check that any one of the following is true: diff --git a/test/account/ExecuteFromPluginPermissions.t.sol b/test/account/ExecuteFromPluginPermissions.t.sol index e8bafa35..8c4936bd 100644 --- a/test/account/ExecuteFromPluginPermissions.t.sol +++ b/test/account/ExecuteFromPluginPermissions.t.sol @@ -314,6 +314,42 @@ contract ExecuteFromPluginPermissionsTest is Test { assertEq(retrievedNumber, 18); } + function test_executeFromPluginExternal_Allowed_AnyContractButSelf() public { + // Run full workflow for counter 1 + + EFPCallerPluginAnyExternal(address(account)).passthroughExecute( + address(counter1), 0, abi.encodeCall(Counter.setNumber, (17)) + ); + uint256 retrievedNumber = counter1.number(); + assertEq(retrievedNumber, 17); + + EFPCallerPluginAnyExternal(address(account)).passthroughExecute( + address(counter1), 0, abi.encodeCall(Counter.increment, ()) + ); + retrievedNumber = counter1.number(); + assertEq(retrievedNumber, 18); + + bytes memory result = EFPCallerPluginAnyExternal(address(account)).passthroughExecute( + address(counter1), 0, abi.encodePacked(bytes4(keccak256("number()"))) + ); + retrievedNumber = abi.decode(result, (uint256)); + assertEq(retrievedNumber, 18); + + // Should fail to call account self + bytes memory encodedCall = + abi.encodeCall(UpgradeableModularAccount.upgradeToAndCall, (address(efpCallerPlugin), "")); + vm.expectRevert( + abi.encodeWithSelector( + UpgradeableModularAccount.ExecFromPluginExternalNotPermitted.selector, + address(efpCallerPluginAnyExternal), + address(account), + 0, + encodedCall + ) + ); + EFPCallerPluginAnyExternal(address(account)).passthroughExecute(address(account), 0, encodedCall); + } + function test_executeFromPluginExternal_NotAllowed_NativeTokenSpending() public { vm.expectRevert( abi.encodeWithSelector( From 1da79ebf6df5b680283dd1709b7842b68e26248e Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Tue, 2 Jan 2024 10:48:24 -0800 Subject: [PATCH 016/106] fix: [quantstamp-1] sessionKey ERC20 spending limit to count approve into limitUsed and ignore existing approve amount (#28) --- .../SessionKeyPermissionsPlugin.sol | 58 +++---------------- .../SessionKeyERC20SpendLimits.t.sol | 10 ++-- 2 files changed, 12 insertions(+), 56 deletions(-) diff --git a/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol b/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol index b9435799..eff8f33c 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol @@ -380,7 +380,7 @@ contract SessionKeyPermissionsPlugin is ISessionKeyPermissionsPlugin, SessionKey // Tally up the amount being spent in each call to an ERC-20 contract. // Since this is a runtime-only check, we can interact with the stored limits after each call in // the batch and can still enforce the limits correctly. - uint256 spendAmount = _getTokenSpendAmount(account, call.target, call.data); + uint256 spendAmount = _getTokenSpendAmount(call.data); if ( !_runtimeUpdateSpendLimitUsage( spendAmount, contractData.erc20SpendLimitTimeInfo, contractData.erc20SpendLimit @@ -597,33 +597,29 @@ contract SessionKeyPermissionsPlugin is ISessionKeyPermissionsPlugin, SessionKey // ERC-20 decoding logic - /// @notice Decode the amount of a token a call is sending. + /// @notice Decode the amount of a token a call is sending/approving. /// @dev This only supports the following standard ERC-20 functions: /// - transfer(address,uint256) - /// - approve(address,uint256) + /// - approve(address,uint256), in this case, the approve amount is always counted towards spending limits even + /// if there are existing aprroval allowances /// When decoding the approve function, this will first check the existing allowance of the spender. This /// lookup is not necessarily in storage associated with the account, so this check should only be used during /// runtime, not user op validation. - /// @param account The account that is sending the transaction. /// @param callData The calldata of the transaction. /// @return The amount of the token being sent. Zero if the call is not recognized as a spend. - function _getTokenSpendAmount(address account, address token, bytes memory callData) - internal - view - returns (uint256) - { + function _getTokenSpendAmount(bytes memory callData) internal pure returns (uint256) { // Get the selector. // Right-padding with zeroes here is OK, because none of the selectors we're comparing this to have // trailing zero bytes. bytes4 selector = bytes4(callData); - if (selector == IERC20.transfer.selector) { + if (selector == IERC20.transfer.selector || selector == IERC20.approve.selector) { // Expected length: 68 bytes (4 selector + 32 address + 32 amount) if (callData.length < 68) { return 0; } - // Load the amount being sent. + // Load the amount being sent/approved. // Solidity doesn't support access a whole word from a bytes memory at once, only a single byte, and // trying to use abi.decode would require copying the data to remove the selector, which is expensive. // Instead, we use inline assembly to load the amount directly. This is safe because we've checked the @@ -634,46 +630,6 @@ contract SessionKeyPermissionsPlugin is ISessionKeyPermissionsPlugin, SessionKey amount := mload(add(callData, 68)) } return amount; - } else if (selector == IERC20.approve.selector) { - // Expected length: 68 bytes (4 selector + 32 address + 32 amount) - if (callData.length < 68) { - return 0; - } - // We must check the existing allowance of the spender. - address spender; - assembly ("memory-safe") { - // Jump 36 words forward: 32 for the length field and 4 for the selector. - spender := mload(add(callData, 36)) - // Mask out the upper 12 bytes of the address, since we only care about the lower 20 bytes. - // If the upper bits are nonzero, typically the token contract should revert as the input is - // malformed. We mask it here only as a precaution for tokens that may not fully conform to the ABI - // standard. - spender := and(spender, shr(96, not(0))) - } - uint256 existingAllowance = IERC20(token).allowance(account, spender); - uint256 approveAmount; - assembly ("memory-safe") { - // Jump 68 words forward: 32 for the length field, 4 for the selector, and 32 for the spender - // address. - approveAmount := mload(add(callData, 68)) - } - // We only consider this spending if the new allowance is greater than the existing allowance. - if (approveAmount <= existingAllowance) { - return 0; - } - - // Return the difference between the new allowance and the existing allowance. - // Unchecked is OK here since we've asserted the new allowance is greater than the existing allowance. - unchecked { - return approveAmount - existingAllowance; - } - - // There is an odd edge-case with the approval amount check. Since multiple approves may be batched, if - // the first approve lowers the allowance but the second one raises it by an amount that's allowed - // within the spend limits, the calls will be permitted. This won't let the session key actually spend - // more than expected, but the spender contract may experience their allowance going down from the - // previous amount by more than the spending limit, then back up to the previous amount plus the - // spending limit. } // Unrecognized function selector return 0; diff --git a/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol b/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol index 545afa5c..5df0f03b 100644 --- a/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol @@ -410,7 +410,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); assertEq(spendLimitInfo.limit, 1 ether); - assertEq(spendLimitInfo.limitUsed, 0.5 ether); + assertEq(spendLimitInfo.limitUsed, 1 ether); assertEq(spendLimitInfo.refreshInterval, 0); // Assert that the last used time is not updated when the interval is unset. assertEq(spendLimitInfo.lastUsedTime, 0); @@ -821,7 +821,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { vm.prank(owner1); SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); - // Run a user op that spends 1 wei, should succeed + // Run a user op that spends 1 wei and approve 1 wei, should succeed Call[] memory calls = new Call[](2); calls[0] = Call({target: address(token1), data: abi.encodeCall(token1.transfer, (recipient1, 1 wei)), value: 0}); @@ -857,13 +857,13 @@ contract SessionKeyERC20SpendLimitsTest is Test { // Run a user op that spends 1 ether, should succeed calls[0] = Call({ target: address(token1), - data: abi.encodeCall(token1.approve, (recipient1, 0.5 ether + 1 wei)), + data: abi.encodeCall(token1.approve, (recipient1, 0.5 ether)), value: 0 }); calls[1] = Call({ target: address(token1), - // previous approved 1 wei is still effective - data: abi.encodeCall(token1.approve, (recipient1, 0.5 ether + 1 wei)), + // previous approved 1 wei should not matter to limitUsed + data: abi.encodeCall(token1.approve, (recipient1, 0.5 ether)), value: 0 }); vm.expectCall(address(token1), 0 wei, calls[0].data, 2); From 60ca631af4eaa1395c642036ea03fadf313625e7 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Wed, 3 Jan 2024 12:19:21 -0500 Subject: [PATCH 017/106] fix: [spearbit-92] make gas limit validation reverts less expensive (#30) --- .../session/permissions/SessionKeyPermissionsPlugin.sol | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol b/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol index eff8f33c..5ff72878 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol @@ -505,7 +505,10 @@ contract SessionKeyPermissionsPlugin is ISessionKeyPermissionsPlugin, SessionKey // We don't have a refresh interval reset, so just check that the gas limits are not exceeded and // update their amounts. validationSuccess = newTotalUsage <= gasLimit; - keyData.gasLimit.limitUsed += newUsage; + if (validationSuccess) { + // Conditionally update as a gas optimization for the failure case. + keyData.gasLimit.limitUsed += newUsage; + } } // RefreshInterval != 0, meaning we have a time period over which the gas limit resets. else if (newTotalUsage <= gasLimit) { From 43d71c690bf6951fa393ff03c008a64646144dd9 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Wed, 3 Jan 2024 12:20:19 -0500 Subject: [PATCH 018/106] fix: [spearbit-89] use newTotalUsage (#31) --- src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol b/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol index 5ff72878..74dfe4c3 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol @@ -507,7 +507,7 @@ contract SessionKeyPermissionsPlugin is ISessionKeyPermissionsPlugin, SessionKey validationSuccess = newTotalUsage <= gasLimit; if (validationSuccess) { // Conditionally update as a gas optimization for the failure case. - keyData.gasLimit.limitUsed += newUsage; + keyData.gasLimit.limitUsed = newTotalUsage; } } // RefreshInterval != 0, meaning we have a time period over which the gas limit resets. From 96f5feab588c79313f59987f1f4f035c1d4d3a0f Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Wed, 3 Jan 2024 10:03:03 -0800 Subject: [PATCH 019/106] fix: [quantstamp-1] sessionKey ERC20 spending limit comments update (#32) --- .../session/permissions/SessionKeyPermissionsPlugin.sol | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol b/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol index 74dfe4c3..8f7792c5 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol @@ -379,7 +379,7 @@ contract SessionKeyPermissionsPlugin is ISessionKeyPermissionsPlugin, SessionKey if (contractData.isERC20WithSpendLimit) { // Tally up the amount being spent in each call to an ERC-20 contract. // Since this is a runtime-only check, we can interact with the stored limits after each call in - // the batch and can still enforce the limits correctly. + // the batch and can still enforce the limits as intended. uint256 spendAmount = _getTokenSpendAmount(call.data); if ( !_runtimeUpdateSpendLimitUsage( @@ -605,9 +605,6 @@ contract SessionKeyPermissionsPlugin is ISessionKeyPermissionsPlugin, SessionKey /// - transfer(address,uint256) /// - approve(address,uint256), in this case, the approve amount is always counted towards spending limits even /// if there are existing aprroval allowances - /// When decoding the approve function, this will first check the existing allowance of the spender. This - /// lookup is not necessarily in storage associated with the account, so this check should only be used during - /// runtime, not user op validation. /// @param callData The calldata of the transaction. /// @return The amount of the token being sent. Zero if the call is not recognized as a spend. function _getTokenSpendAmount(bytes memory callData) internal pure returns (uint256) { From 3e8944fcd48df75085088a24f692530f6735eab4 Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Wed, 3 Jan 2024 14:07:17 -0800 Subject: [PATCH 020/106] fix: [spearbit-99] Correctly set post-only hook flag (#20) --- src/account/PluginManagerInternals.sol | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/account/PluginManagerInternals.sol b/src/account/PluginManagerInternals.sol index 59421160..dd67ef0f 100644 --- a/src/account/PluginManagerInternals.sol +++ b/src/account/PluginManagerInternals.sol @@ -132,9 +132,8 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { if (preExecHook != FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE) { selectorData.hasPreExecHooks = true; - } - - if (postExecHook != FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE) { + } else if (postExecHook != FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE) { + // Only set this flag if the pre hook is empty and the post hook is non-empty. selectorData.hasPostOnlyExecHooks = true; } } @@ -144,14 +143,14 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { { SelectorData storage selectorData = _getAccountStorage().selectorData[selector]; - (bool shouldClearHasPreHooks, bool shouldClearHasPostHooks) = + (bool shouldClearHasPreHooks, bool shouldClearHasPostOnlyHooks) = _removeHooks(selectorData.executionHooks, preExecHook, postExecHook); if (shouldClearHasPreHooks) { selectorData.hasPreExecHooks = false; } - if (shouldClearHasPostHooks) { + if (shouldClearHasPostOnlyHooks) { selectorData.hasPostOnlyExecHooks = false; } } @@ -178,9 +177,8 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { if (preExecHook != FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE) { permittedCallData.hasPrePermittedCallHooks = true; - } - - if (postExecHook != FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE) { + } else if (postExecHook != FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE) { + // Only set this flag if the pre hook is empty and the post hook is non-empty. permittedCallData.hasPostOnlyPermittedCallHooks = true; } } @@ -194,14 +192,14 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { PermittedCallData storage permittedCallData = _getAccountStorage().permittedCalls[_getPermittedCallKey(plugin, selector)]; - (bool shouldClearHasPreHooks, bool shouldClearHasPostHooks) = + (bool shouldClearHasPreHooks, bool shouldClearHasPostOnlyHooks) = _removeHooks(permittedCallData.permittedCallHooks, preExecHook, postExecHook); if (shouldClearHasPreHooks) { permittedCallData.hasPrePermittedCallHooks = false; } - if (shouldClearHasPostHooks) { + if (shouldClearHasPostOnlyHooks) { permittedCallData.hasPostOnlyPermittedCallHooks = false; } } @@ -239,7 +237,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { function _removeHooks(HookGroup storage hooks, FunctionReference preExecHook, FunctionReference postExecHook) internal - returns (bool shouldClearHasPreHooks, bool shouldClearHasPostHooks) + returns (bool shouldClearHasPreHooks, bool shouldClearHasPostOnlyHooks) { if (preExecHook != FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE) { // If decrementing results in removal, this also clears the flag _PRE_EXEC_HOOK_HAS_POST_FLAG. @@ -273,7 +271,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { // Update the cached flag value for the post-only exec hooks, as it may change with a removal. if (hooks.postOnlyHooks.isEmpty()) { // The "has post only hooks" flag should be disabled - shouldClearHasPostHooks = true; + shouldClearHasPostOnlyHooks = true; } } } From 4016fe86d83e47697108661e724ca09b56d8abd4 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Thu, 4 Jan 2024 19:33:44 -0500 Subject: [PATCH 021/106] fix: [spearbit-95][quantstamp-9] disallow self-dependencies (#33) --- src/account/PluginManagerInternals.sol | 32 ++++---- ...gradeableModularAccountPluginManager.t.sol | 81 ++++++++++++++----- 2 files changed, 79 insertions(+), 34 deletions(-) diff --git a/src/account/PluginManagerInternals.sol b/src/account/PluginManagerInternals.sol index dd67ef0f..66f42b40 100644 --- a/src/account/PluginManagerInternals.sol +++ b/src/account/PluginManagerInternals.sol @@ -359,7 +359,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { // Check the dependency interface id over the address of the dependency. (address dependencyAddr,) = dependencies[i].unpack(); - // Check that the dependency is installed. + // Check that the dependency is installed. This also blocks self-dependencies. if (storage_.pluginData[dependencyAddr].manifestHash == bytes32(0)) { revert MissingPluginDependency(dependencyAddr); } @@ -377,17 +377,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { } } - // Add the plugin metadata to the account - storage_.pluginData[plugin].manifestHash = manifestHash; - storage_.pluginData[plugin].dependencies = dependencies; - // Update components according to the manifest. - // All conflicts should revert. - - // Mark whether or not this plugin may spend native token amounts - if (manifest.canSpendNativeToken) { - storage_.pluginData[plugin].canSpendNativeToken = true; - } // Install execution functions length = manifest.executionFunctions.length; @@ -452,7 +442,12 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { for (uint256 i = 0; i < length;) { InjectedHook memory hook = injectedHooks[i]; - storage_.pluginData[plugin].injectedHooks[i] = StoredInjectedHook({ + // Check that the dependency is installed. This also blocks self-dependencies. + if (storage_.pluginData[hook.providingPlugin].manifestHash == bytes32(0)) { + revert MissingPluginDependency(hook.providingPlugin); + } + + injectedHooksArray[i] = StoredInjectedHook({ providingPlugin: hook.providingPlugin, selector: hook.selector, preExecHookFunctionId: hook.injectedHooksInfo.preExecHookFunctionId, @@ -463,10 +458,6 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { // Increment the dependent count for the plugin providing the hook. storage_.pluginData[hook.providingPlugin].dependentCount += 1; - if (!storage_.plugins.contains(CastLib.toSetValue(hook.providingPlugin))) { - revert MissingPluginDependency(hook.providingPlugin); - } - _addPermittedCallHooks( hook.selector, plugin, @@ -606,6 +597,15 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { } } + // Add the plugin metadata to the account + storage_.pluginData[plugin].manifestHash = manifestHash; + storage_.pluginData[plugin].dependencies = dependencies; + + // Mark whether or not this plugin may spend native token amounts + if (manifest.canSpendNativeToken) { + storage_.pluginData[plugin].canSpendNativeToken = true; + } + // Call injected hooks' onHookApply after all setup, this is before calling plugin onInstall length = injectedHooks.length; for (uint256 i = 0; i < length;) { diff --git a/test/account/UpgradeableModularAccountPluginManager.t.sol b/test/account/UpgradeableModularAccountPluginManager.t.sol index c848b6ff..706b0c32 100644 --- a/test/account/UpgradeableModularAccountPluginManager.t.sol +++ b/test/account/UpgradeableModularAccountPluginManager.t.sol @@ -313,31 +313,36 @@ contract UpgradeableModularAccountPluginManagerTest is Test { function test_installPlugin_missingDependency() public { vm.startPrank(owner2); - address[] memory guardians = new address[](1); - guardians[0] = address(1); - - bytes32 manifestHash = keccak256(abi.encode(sessionKeyPlugin.pluginManifest())); - - // Create a duplicate MultiOwnerPlugin that isn't installed, and attempt to use that as a dependency - MultiOwnerPlugin multiOwnerPlugin2 = new MultiOwnerPlugin(); + manifest.dependencyInterfaceIds.push(type(IPlugin).interfaceId); + MockPlugin newPlugin = new MockPlugin(manifest); + bytes32 manifestHash = keccak256(abi.encode(newPlugin.pluginManifest())); - FunctionReference[] memory dependencies = new FunctionReference[](2); - dependencies[0] = FunctionReferenceLib.pack( - address(multiOwnerPlugin2), uint8(IMultiOwnerPlugin.FunctionId.USER_OP_VALIDATION_OWNER) - ); - dependencies[1] = FunctionReferenceLib.pack( - address(multiOwnerPlugin2), uint8(IMultiOwnerPlugin.FunctionId.RUNTIME_VALIDATION_OWNER_OR_SELF) + // Add invalid function reference that points to the plugin being installed, rather than + // an existing dependency. + FunctionReference[] memory dependencies = new FunctionReference[](1); + dependencies[0] = FunctionReferenceLib.pack(address(newPlugin), 0); + vm.expectRevert( + abi.encodeWithSelector(PluginManagerInternals.MissingPluginDependency.selector, address(newPlugin)) ); + IPluginManager(account2).installPlugin({ + plugin: address(newPlugin), + manifestHash: manifestHash, + pluginInitData: "", + dependencies: dependencies, + injectedHooks: new IPluginManager.InjectedHook[](0) + }); + // Add invalid function reference that points to a plugin that is not yet installed (and also is not the + // one currently being installed). + MockPlugin newPlugin2 = new MockPlugin(manifest); + dependencies[0] = FunctionReferenceLib.pack(address(newPlugin2), 0); vm.expectRevert( - abi.encodeWithSelector( - PluginManagerInternals.MissingPluginDependency.selector, address(multiOwnerPlugin2) - ) + abi.encodeWithSelector(PluginManagerInternals.MissingPluginDependency.selector, address(newPlugin2)) ); IPluginManager(account2).installPlugin({ - plugin: address(sessionKeyPlugin), + plugin: address(newPlugin), manifestHash: manifestHash, - pluginInitData: abi.encode(uint48(1 days)), + pluginInitData: "", dependencies: dependencies, injectedHooks: new IPluginManager.InjectedHook[](0) }); @@ -759,6 +764,46 @@ contract UpgradeableModularAccountPluginManagerTest is Test { }); } + function test_injectHooksMissingDependency() external { + vm.startPrank(owner2); + + MockPlugin newPlugin = new MockPlugin(manifest); + bytes32 manifestHash = keccak256(abi.encode(newPlugin.pluginManifest())); + + // Add invalid injected hook that points to the plugin being installed, rather than an existing dependency. + IPluginManager.InjectedHook[] memory hooks = new IPluginManager.InjectedHook[](1); + hooks[0] = IPluginManager.InjectedHook( + address(newPlugin), IPluginExecutor.executeFromPluginExternal.selector, injectedHooksInfo, "" + ); + vm.expectRevert( + abi.encodeWithSelector(PluginManagerInternals.MissingPluginDependency.selector, address(newPlugin)) + ); + IPluginManager(account2).installPlugin({ + plugin: address(newPlugin), + manifestHash: manifestHash, + pluginInitData: "", + dependencies: new FunctionReference[](0), + injectedHooks: hooks + }); + + // Add invalid injected hook that points to a plugin that is not yet installed (and also is not the one + // currently being installed). + MockPlugin newPlugin2 = new MockPlugin(manifest); + hooks[0] = IPluginManager.InjectedHook( + address(newPlugin2), IPluginExecutor.executeFromPluginExternal.selector, injectedHooksInfo, "" + ); + vm.expectRevert( + abi.encodeWithSelector(PluginManagerInternals.MissingPluginDependency.selector, address(newPlugin2)) + ); + IPluginManager(account2).installPlugin({ + plugin: address(newPlugin), + manifestHash: manifestHash, + pluginInitData: "", + dependencies: new FunctionReference[](0), + injectedHooks: hooks + }); + } + function test_injectHooksUninstall() external { (, MockPlugin newPlugin,) = _installWithInjectHooks(); From dec1d76bf1916c32977679ea4161fe28c5fc87ef Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Thu, 4 Jan 2024 20:38:03 -0800 Subject: [PATCH 022/106] fix: [spearbit-51][quantstamp-14] clean up upper bits of address in PluginStorageLib (#35) --- src/libraries/PluginStorageLib.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/PluginStorageLib.sol b/src/libraries/PluginStorageLib.sol index 949f78a2..e600561e 100644 --- a/src/libraries/PluginStorageLib.sol +++ b/src/libraries/PluginStorageLib.sol @@ -31,6 +31,8 @@ library PluginStorageLib { mstore(0x40, add(add(key, totalSize), 32)) mstore(key, totalSize) + // Clear any dirty upper bits of address + addr := and(addr, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) // Store the address and batch index in the key buffer mstore(add(key, 32), addr) mstore(add(key, 64), batchIndex) From 662445a666e1356bab8fda558669c516be7a1cd1 Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Fri, 5 Jan 2024 21:22:12 -0800 Subject: [PATCH 023/106] fix: [spearbit-54] use salt in eip-712 domain separator (#38) --- src/plugins/owner/MultiOwnerPlugin.sol | 33 ++++++++++++++---------- test/plugin/owner/MultiOwnerPlugin.t.sol | 4 +-- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/plugins/owner/MultiOwnerPlugin.sol b/src/plugins/owner/MultiOwnerPlugin.sol index 47588c2f..8ea4b871 100644 --- a/src/plugins/owner/MultiOwnerPlugin.sol +++ b/src/plugins/owner/MultiOwnerPlugin.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.21; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol"; import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; @@ -45,7 +44,7 @@ import {SetValue} from "../../libraries/LinkedListSetUtils.sol"; /// the account, violating storage access rules. This also means that the /// owner of a modular account may not be another modular account if you want to /// send user operations through a bundler. -contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271, EIP712 { +contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { using AssociatedLinkedListSetLib for AssociatedLinkedListSet; using ECDSA for bytes32; @@ -53,12 +52,12 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271, EIP712 { string internal constant _VERSION = "1.0.0"; string internal constant _AUTHOR = "Alchemy"; - bytes32 private constant _TYPE_HASH = - keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); - bytes32 private immutable _HASHED_NAME = keccak256(bytes(_NAME)); - bytes32 private immutable _HASHED_VERSION = keccak256(bytes(_VERSION)); - - constructor() EIP712(_NAME, _VERSION) {} + bytes32 private constant _TYPE_HASH = keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)" + ); + bytes32 private constant _HASHED_NAME = keccak256(bytes(_NAME)); + bytes32 private constant _HASHED_VERSION = keccak256(bytes(_VERSION)); + bytes32 private immutable _SALT = bytes32(bytes20(address(this))); // ERC-4337 specific value: signature validation passed uint256 internal constant _SIG_VALIDATION_PASSED = 0; @@ -111,7 +110,7 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271, EIP712 { function eip712Domain() public view - override(IMultiOwnerPlugin, EIP712) + override returns ( bytes1 fields, string memory name, @@ -122,8 +121,15 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271, EIP712 { uint256[] memory extensions ) { - (fields, name, version, chainId,, salt, extensions) = super.eip712Domain(); - verifyingContract = msg.sender; + return ( + hex"1f", // 11111 indicate salt field is also used + _NAME, + _VERSION, + block.chainid, + msg.sender, + _SALT, + new uint256[](0) + ); } /// @inheritdoc IERC1271 @@ -134,8 +140,7 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271, EIP712 { /// an "Ethereum Signed Message" envelope before checking the signature in /// the EOA-owner case. function isValidSignature(bytes32 digest, bytes memory signature) public view override returns (bytes4) { - bytes memory messageData = encodeMessageData(msg.sender, abi.encode(digest)); - bytes32 messageHash = keccak256(messageData); + bytes32 messageHash = getMessageHash(msg.sender, abi.encode(digest)); // try to recover through ECDSA (address signer, ECDSA.RecoverError error) = ECDSA.tryRecover(messageHash, signature); @@ -373,7 +378,7 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271, EIP712 { // ┗━━━━━━━━━━━━━━━┛ function _domainSeparator(address account) internal view returns (bytes32) { - return keccak256(abi.encode(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION, block.chainid, account)); + return keccak256(abi.encode(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION, block.chainid, account, _SALT)); } function _addOwnersOrRevert( diff --git a/test/plugin/owner/MultiOwnerPlugin.t.sol b/test/plugin/owner/MultiOwnerPlugin.t.sol index 12f9bbd0..b6a29593 100644 --- a/test/plugin/owner/MultiOwnerPlugin.t.sol +++ b/test/plugin/owner/MultiOwnerPlugin.t.sol @@ -101,12 +101,12 @@ contract MultiOwnerPluginTest is Test { bytes32 salt, uint256[] memory extensions ) = plugin.eip712Domain(); - assertEq(fields, hex"0f"); + assertEq(fields, hex"1f"); assertEq(name, "Multi Owner Plugin"); assertEq(version, "1.0.0"); assertEq(chainId, block.chainid); assertEq(verifyingContract, accountA); - assertEq(salt, bytes32(0)); + assertEq(salt, bytes32(bytes20(address(plugin)))); assertEq(extensions.length, 0); } From 2a22fbc187958f99785297a8475aed4f69f0d744 Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Fri, 5 Jan 2024 21:23:01 -0800 Subject: [PATCH 024/106] fix: [quantstamp-6] add runtime deny pre hook for session key plugin (#34) --- src/plugins/session/SessionKeyPlugin.sol | 13 +++++++++++-- .../permissions/SessionKeyPermissionsPlugin.sol | 2 +- .../SessionKeyPluginWithMultiOwner.t.sol | 17 +++++++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/plugins/session/SessionKeyPlugin.sol b/src/plugins/session/SessionKeyPlugin.sol index 041ac61f..ce803de3 100644 --- a/src/plugins/session/SessionKeyPlugin.sol +++ b/src/plugins/session/SessionKeyPlugin.sol @@ -243,8 +243,7 @@ contract SessionKeyPlugin is BasePlugin, ISessionKeyPlugin { }); // Session keys are only expected to be used for user op validation, so no runtime validation functions are - // set over - // executeWithSessionKey. + // set over executeWithSessionKey, and pre runtime hook will always deny. ManifestFunction memory alwaysAllowValidationFunction = ManifestFunction({ functionType: ManifestAssociatedFunctionType.RUNTIME_VALIDATION_ALWAYS_ALLOW, functionId: 0, // Unused. @@ -271,6 +270,16 @@ contract SessionKeyPlugin is BasePlugin, ISessionKeyPlugin { associatedFunction: ownerRuntimeValidationFunction }); + manifest.preRuntimeValidationHooks = new ManifestAssociatedFunction[](1); + manifest.preRuntimeValidationHooks[0] = ManifestAssociatedFunction({ + executionSelector: this.executeWithSessionKey.selector, + associatedFunction: ManifestFunction({ + functionType: ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY, + functionId: 0, + dependencyIndex: 0 + }) + }); + manifest.permitAnyExternalAddress = true; manifest.canSpendNativeToken = true; diff --git a/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol b/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol index 8f7792c5..1e570d08 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol @@ -604,7 +604,7 @@ contract SessionKeyPermissionsPlugin is ISessionKeyPermissionsPlugin, SessionKey /// @dev This only supports the following standard ERC-20 functions: /// - transfer(address,uint256) /// - approve(address,uint256), in this case, the approve amount is always counted towards spending limits even - /// if there are existing aprroval allowances + /// if there are existing approval allowances /// @param callData The calldata of the transaction. /// @return The amount of the token being sent. Zero if the call is not recognized as a spend. function _getTokenSpendAmount(bytes memory callData) internal pure returns (uint256) { diff --git a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol index 0e39b234..3a5b57f4 100644 --- a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol +++ b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol @@ -162,6 +162,23 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { assertEq(recipient.balance, 2 wei); } + function test_sessionKey_useSessionKey_failInRuntime() public { + address[] memory sessionKeysToAdd = new address[](1); + (address sessionKey,) = makeAddrAndKey("sessionKey1"); + sessionKeysToAdd[0] = sessionKey; + + vm.startPrank(owner1); + SessionKeyPlugin(address(account1)).updateSessionKeys( + sessionKeysToAdd, new SessionKeyPlugin.SessionKeyToRemove[](0) + ); + + Call[] memory calls = new Call[](1); + calls[0] = Call({target: recipient, value: 1 wei, data: ""}); + + vm.expectRevert(abi.encodeWithSelector(UpgradeableModularAccount.AlwaysDenyRule.selector)); + SessionKeyPlugin(address(account1)).executeWithSessionKey(calls, sessionKey); + } + function testFuzz_sessionKey_userOpValidation_valid(uint16 seed) public { uint256[] memory privateKeys = _createSessionKeys(uint8(seed)); From 596ad999786912afb5c6a70ce4a0d94b16cd36d4 Mon Sep 17 00:00:00 2001 From: David Philipson Date: Sun, 7 Jan 2024 14:54:57 -0500 Subject: [PATCH 025/106] fix: [spearbit-25] Function initialize() can use calldata (#18) --- src/account/UpgradeableModularAccount.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index d1ab7cc5..e3712b3d 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -96,7 +96,7 @@ contract UpgradeableModularAccount is // EXTERNAL FUNCTIONS /// @inheritdoc IAccountInitializable - function initialize(address[] memory plugins, bytes calldata pluginInitData) external initializer { + function initialize(address[] calldata plugins, bytes calldata pluginInitData) external initializer { (bytes32[] memory manifestHashes, bytes[] memory pluginInstallDatas) = abi.decode(pluginInitData, (bytes32[], bytes[])); From 5d0816a52ab770a3f62043e1fef3477c04ef0573 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Sun, 7 Jan 2024 15:17:12 -0500 Subject: [PATCH 026/106] chore: add optimized-build profile (#36) --- .gitignore | 3 ++- README.md | 3 +++ foundry.toml | 8 +++++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index b8896348..6bbaff28 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Foundry build and cache directories out/ +out-optimized/ cache/ node_modules/ @@ -8,4 +9,4 @@ report/ lcov.info # secret -.env \ No newline at end of file +.env diff --git a/README.md b/README.md index 1b4018e5..9822f627 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,9 @@ forge build # or use the lite profile to reduce compilation time FOUNDRY_PROFILE=lite forge build + +# for faster IR builds (to check contract sizes) +FOUNDRY_PROFILE=optimized-build forge build --sizes ``` ## Syntax check diff --git a/foundry.toml b/foundry.toml index 2d276cd1..ffb522b1 100644 --- a/foundry.toml +++ b/foundry.toml @@ -17,6 +17,12 @@ runs=500 fail_on_revert = true depth = 10 +[profile.optimized-build] +via_ir = true +script = 'src' +test = 'src' +out = 'out-optimized' + [profile.lite] solc = '0.8.21' via_ir = false @@ -44,4 +50,4 @@ goerli = "${GOERLI_RPC_URL}" mainnet = { key = "${ETHERSCAN_API_KEY}" } goerli = { key = "${ETHERSCAN_API_KEY}" } -# See more config options https://github.com/foundry-rs/foundry/tree/master/config \ No newline at end of file +# See more config options https://github.com/foundry-rs/foundry/tree/master/config From be5f7d1af927ebc5ffbb96a2d1bf7f08a92c4828 Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Sun, 7 Jan 2024 12:19:12 -0800 Subject: [PATCH 027/106] style: forge fmt and update foundry version in CI (#37) --- .github/workflows/test.yml | 6 +++--- src/plugins/owner/IMultiOwnerPlugin.sol | 1 + src/plugins/session/ISessionKeyPlugin.sol | 4 +++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 68325d4d..34faa933 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,7 +20,7 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: - version: nightly-5be158ba6dc7c798a6f032026fe60fc01686b33b + version: nightly-6fc74638b797b8e109452d3df8e26758f86f31fe - name: "Install Pnpm" uses: "pnpm/action-setup@v2" @@ -72,7 +72,7 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: - version: nightly-5be158ba6dc7c798a6f032026fe60fc01686b33b + version: nightly-6fc74638b797b8e109452d3df8e26758f86f31fe - name: Install forge dependencies run: forge install @@ -95,7 +95,7 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: - version: nightly-5be158ba6dc7c798a6f032026fe60fc01686b33b + version: nightly-6fc74638b797b8e109452d3df8e26758f86f31fe - name: Install forge dependencies run: forge install diff --git a/src/plugins/owner/IMultiOwnerPlugin.sol b/src/plugins/owner/IMultiOwnerPlugin.sol index ac7b48c7..4cdfe1d8 100644 --- a/src/plugins/owner/IMultiOwnerPlugin.sol +++ b/src/plugins/owner/IMultiOwnerPlugin.sol @@ -7,6 +7,7 @@ interface IMultiOwnerPlugin { enum FunctionId { RUNTIME_VALIDATION_OWNER_OR_SELF, // require owner or self access USER_OP_VALIDATION_OWNER // require owner access + } /// @notice This event is emitted when owners of the account are updated. diff --git a/src/plugins/session/ISessionKeyPlugin.sol b/src/plugins/session/ISessionKeyPlugin.sol index 713fb90d..3eaf95cb 100644 --- a/src/plugins/session/ISessionKeyPlugin.sol +++ b/src/plugins/session/ISessionKeyPlugin.sol @@ -5,7 +5,9 @@ import {Call} from "../../interfaces/IStandardExecutor.sol"; import {UserOperation} from "../../interfaces/erc4337/UserOperation.sol"; interface ISessionKeyPlugin { - enum FunctionId {USER_OP_VALIDATION_SESSION_KEY} + enum FunctionId { + USER_OP_VALIDATION_SESSION_KEY + } error InvalidSessionKey(address sessionKey); error NotAuthorized(address caller); From a46d9cb2eab94ef41eedb0daebdf9ee7cec46f29 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Mon, 8 Jan 2024 15:37:08 -0500 Subject: [PATCH 028/106] chore: bump foundry version (#44) --- .github/workflows/test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 34faa933..853af473 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,7 +20,7 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: - version: nightly-6fc74638b797b8e109452d3df8e26758f86f31fe + version: nightly-02292f2d2caa547968bd039c06dc53d98b72bf39 - name: "Install Pnpm" uses: "pnpm/action-setup@v2" @@ -72,7 +72,7 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: - version: nightly-6fc74638b797b8e109452d3df8e26758f86f31fe + version: nightly-02292f2d2caa547968bd039c06dc53d98b72bf39 - name: Install forge dependencies run: forge install @@ -95,7 +95,7 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: - version: nightly-6fc74638b797b8e109452d3df8e26758f86f31fe + version: nightly-02292f2d2caa547968bd039c06dc53d98b72bf39 - name: Install forge dependencies run: forge install From c67dcb46f1582f971db5c7ff29c3a7eb6150e8f8 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Mon, 8 Jan 2024 17:27:56 -0500 Subject: [PATCH 029/106] fix: [spearbit-86-41] use cached variables accessControlType, callLength (#39) --- .../SessionKeyPermissionsPlugin.sol | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol b/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol index 1e570d08..1350e97c 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol @@ -259,17 +259,18 @@ contract SessionKeyPermissionsPlugin is ISessionKeyPermissionsPlugin, SessionKey uint256 callsLength = calls.length; // Only return validation success when there is at least one call - bool validationSuccess = calls.length > 0; - for (uint256 i = 0; i < callsLength;) { - Call memory call = calls[i]; - nativeTokenSpend += call.value; - validationSuccess = validationSuccess - && _checkCallPermissions( - sessionKeyData.contractAccessControlType, keyId, call.target, call.value, call.data - ); - - unchecked { - ++i; + bool validationSuccess = callsLength > 0; + { + ContractAccessControlType accessControlType = sessionKeyData.contractAccessControlType; + for (uint256 i = 0; i < callsLength;) { + Call memory call = calls[i]; + nativeTokenSpend += call.value; + validationSuccess = validationSuccess + && _checkCallPermissions(accessControlType, keyId, call.target, call.value, call.data); + + unchecked { + ++i; + } } } From 27fe9de783242c1b14de2f792d501858dfdb91c6 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Mon, 8 Jan 2024 17:28:29 -0500 Subject: [PATCH 030/106] fix: [spearbit-72] cache dependencies.length earlier (#40) --- src/account/PluginManagerInternals.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/account/PluginManagerInternals.sol b/src/account/PluginManagerInternals.sol index 66f42b40..7a0c0751 100644 --- a/src/account/PluginManagerInternals.sol +++ b/src/account/PluginManagerInternals.sol @@ -350,11 +350,11 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { } // Check that the dependencies match the manifest. - if (dependencies.length != manifest.dependencyInterfaceIds.length) { + uint256 length = dependencies.length; + if (length != manifest.dependencyInterfaceIds.length) { revert InvalidDependenciesProvided(); } - uint256 length = dependencies.length; for (uint256 i = 0; i < length;) { // Check the dependency interface id over the address of the dependency. (address dependencyAddr,) = dependencies[i].unpack(); From 723ef749a46ac38b1b76edf27ac4c1312b97e992 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Mon, 8 Jan 2024 17:29:33 -0500 Subject: [PATCH 031/106] fix: [spearbit-66] remove redundant 0 check (#41) --- src/plugins/session/ISessionKeyPlugin.sol | 1 - src/plugins/session/SessionKeyPlugin.sol | 14 +++++--------- .../SessionKeyPluginWithMultiOwner.t.sol | 18 ++++++++++++++++++ 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/plugins/session/ISessionKeyPlugin.sol b/src/plugins/session/ISessionKeyPlugin.sol index 3eaf95cb..6f71cf95 100644 --- a/src/plugins/session/ISessionKeyPlugin.sol +++ b/src/plugins/session/ISessionKeyPlugin.sol @@ -11,7 +11,6 @@ interface ISessionKeyPlugin { error InvalidSessionKey(address sessionKey); error NotAuthorized(address caller); - error SessionKeyAlreadyExists(address sessionKey); error SessionKeyNotFound(address sessionKey); error UnableToRemove(address sessionKey); diff --git a/src/plugins/session/SessionKeyPlugin.sol b/src/plugins/session/SessionKeyPlugin.sol index ce803de3..dfaa8803 100644 --- a/src/plugins/session/SessionKeyPlugin.sol +++ b/src/plugins/session/SessionKeyPlugin.sol @@ -105,8 +105,9 @@ contract SessionKeyPlugin is BasePlugin, ISessionKeyPlugin { length = sessionKeysToAdd.length; for (uint256 i = 0; i < length;) { + // This also checks that sessionKeysToAdd[i] is not zero. if (!_sessionKeys.tryAdd(msg.sender, CastLib.toSetValue(sessionKeysToAdd[i]))) { - revert SessionKeyAlreadyExists(sessionKeysToAdd[i]); + revert InvalidSessionKey(sessionKeysToAdd[i]); } unchecked { @@ -184,14 +185,9 @@ contract SessionKeyPlugin is BasePlugin, ISessionKeyPlugin { uint256 length = sessionKeysToAdd.length; for (uint256 i = 0; i < length;) { - address sessionKey = sessionKeysToAdd[i]; - - if (sessionKey == address(0)) { - revert InvalidSessionKey(sessionKey); - } - - if (!_sessionKeys.tryAdd(msg.sender, CastLib.toSetValue(sessionKey))) { - revert SessionKeyAlreadyExists(sessionKeysToAdd[i]); + // This also checks that sessionKeysToAdd[i] is not zero. + if (!_sessionKeys.tryAdd(msg.sender, CastLib.toSetValue(sessionKeysToAdd[i]))) { + revert InvalidSessionKey(sessionKeysToAdd[i]); } unchecked { diff --git a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol index 3a5b57f4..98ba248e 100644 --- a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol +++ b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol @@ -97,6 +97,24 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { assertEq(sessionKeys[0], sessionKeysToAdd[0]); } + function test_sessionKey_addKeyFailure() public { + vm.startPrank(owner1); + + address[] memory sessionKeysToAdd = new address[](1); + vm.expectRevert(abi.encodeWithSelector(ISessionKeyPlugin.InvalidSessionKey.selector, address(0))); + SessionKeyPlugin(address(account1)).updateSessionKeys( + sessionKeysToAdd, new SessionKeyPlugin.SessionKeyToRemove[](0) + ); + + sessionKeysToAdd = new address[](2); + sessionKeysToAdd[0] = makeAddr("sessionKey1"); + sessionKeysToAdd[1] = sessionKeysToAdd[0]; + vm.expectRevert(abi.encodeWithSelector(ISessionKeyPlugin.InvalidSessionKey.selector, sessionKeysToAdd[0])); + SessionKeyPlugin(address(account1)).updateSessionKeys( + sessionKeysToAdd, new SessionKeyPlugin.SessionKeyToRemove[](0) + ); + } + function test_sessionKey_addAndRemoveKeys() public { address[] memory sessionKeysToAdd = new address[](2); sessionKeysToAdd[0] = makeAddr("sessionKey1"); From 2b9c5d9c48827ac63e25175bb2b3920c0ba403b5 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Mon, 8 Jan 2024 17:30:39 -0500 Subject: [PATCH 032/106] fix: [spearbit-62] cache owners_.length (#42) --- src/plugins/owner/MultiOwnerPlugin.sol | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/plugins/owner/MultiOwnerPlugin.sol b/src/plugins/owner/MultiOwnerPlugin.sol index 8ea4b871..4d1a9807 100644 --- a/src/plugins/owner/MultiOwnerPlugin.sol +++ b/src/plugins/owner/MultiOwnerPlugin.sol @@ -403,14 +403,14 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { address associated, address[] memory ownersToRemove ) private { - uint256 ownersToRemoveLength = ownersToRemove.length; - for (uint256 j; j < ownersToRemoveLength;) { - if (!ownerSet.tryRemove(associated, CastLib.toSetValue(ownersToRemove[j]))) { - revert OwnerDoesNotExist(ownersToRemove[j]); + uint256 length = ownersToRemove.length; + for (uint256 i; i < length;) { + if (!ownerSet.tryRemove(associated, CastLib.toSetValue(ownersToRemove[i]))) { + revert OwnerDoesNotExist(ownersToRemove[i]); } unchecked { - ++j; + ++i; } } } @@ -421,7 +421,8 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { returns (bool) { address[] memory owners_ = ownersOf(associated); - for (uint256 i; i < owners_.length;) { + uint256 length = owners_.length; + for (uint256 i; i < length;) { if (SignatureChecker.isValidERC1271SignatureNow(owners_[i], digest, signature)) { return true; } From a68a807d29beb94ca9177ab2106504a3bfc21210 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Mon, 8 Jan 2024 17:31:22 -0500 Subject: [PATCH 033/106] fix: [spearbit-46] remove extra argument from _checkCallPermissions() (#43) --- .../session/permissions/SessionKeyPermissionsPlugin.sol | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol b/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol index 1350e97c..cb1d8a09 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol @@ -265,8 +265,8 @@ contract SessionKeyPermissionsPlugin is ISessionKeyPermissionsPlugin, SessionKey for (uint256 i = 0; i < callsLength;) { Call memory call = calls[i]; nativeTokenSpend += call.value; - validationSuccess = validationSuccess - && _checkCallPermissions(accessControlType, keyId, call.target, call.value, call.data); + validationSuccess = + validationSuccess && _checkCallPermissions(accessControlType, keyId, call.target, call.data); unchecked { ++i; @@ -336,7 +336,6 @@ contract SessionKeyPermissionsPlugin is ISessionKeyPermissionsPlugin, SessionKey ContractAccessControlType accessControlType, SessionKeyId keyId, address target, - uint256, /*value*/ bytes memory callData ) internal view returns (bool validationSuccess) { validationSuccess = true; From 98094b5eb836d8f386d0837b08ccab93ecd65f20 Mon Sep 17 00:00:00 2001 From: David Philipson Date: Mon, 8 Jan 2024 18:37:24 -0500 Subject: [PATCH 034/106] fix: [quantstamp-21] prevent adding IPlugin interface to account (#45) --- src/account/PluginManagerInternals.sol | 7 ++++++- ...gradeableModularAccountPluginManager.t.sol | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/account/PluginManagerInternals.sol b/src/account/PluginManagerInternals.sol index 7a0c0751..c4a4a8a8 100644 --- a/src/account/PluginManagerInternals.sol +++ b/src/account/PluginManagerInternals.sol @@ -51,6 +51,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { error ExecutionFunctionAlreadySet(bytes4 selector); error ExecutionFunctionNotSet(bytes4 selector); error IPluginFunctionNotAllowed(bytes4 selector); + error IPluginInterfaceNotAllowed(); error InvalidDependenciesProvided(); error InvalidPluginManifest(); error MissingPluginDependency(address dependency); @@ -591,7 +592,11 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { // Add new interface ids the plugin enabled for the account length = manifest.interfaceIds.length; for (uint256 i = 0; i < length;) { - storage_.supportedInterfaces[manifest.interfaceIds[i]] += 1; + bytes4 interfaceId = manifest.interfaceIds[i]; + if (interfaceId == type(IPlugin).interfaceId) { + revert IPluginInterfaceNotAllowed(); + } + storage_.supportedInterfaces[interfaceId] += 1; unchecked { ++i; } diff --git a/test/account/UpgradeableModularAccountPluginManager.t.sol b/test/account/UpgradeableModularAccountPluginManager.t.sol index 706b0c32..ac1ce1b7 100644 --- a/test/account/UpgradeableModularAccountPluginManager.t.sol +++ b/test/account/UpgradeableModularAccountPluginManager.t.sol @@ -286,6 +286,25 @@ contract UpgradeableModularAccountPluginManagerTest is Test { }); } + function test_installPlugin_failIfAddingIPluginInterfaceId() public { + vm.startPrank(owner2); + + PluginManifest memory manifestBad; + manifestBad.interfaceIds = new bytes4[](1); + manifestBad.interfaceIds[0] = type(IPlugin).interfaceId; + MockPlugin mockPluginBad = new MockPlugin(manifestBad); + bytes32 manifestHashBad = keccak256(abi.encode(mockPluginBad.pluginManifest())); + + vm.expectRevert(PluginManagerInternals.IPluginInterfaceNotAllowed.selector); + IPluginManager(account2).installPlugin({ + plugin: address(mockPluginBad), + manifestHash: manifestHashBad, + pluginInitData: bytes(""), + dependencies: new FunctionReference[](0), + injectedHooks: new IPluginManager.InjectedHook[](0) + }); + } + function test_installPlugin_failWtihErc4337FunctionSelector() public { vm.startPrank(owner2); From 9ea511278c0d85ee86c957b09499ca34d87a35b9 Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Tue, 9 Jan 2024 13:20:40 -0800 Subject: [PATCH 035/106] fix: [spearbit-107] [quantstamp-22] revert UO validation when error is not sig mismatch in both core account and session key plugin (#46) --- src/account/AccountExecutor.sol | 5 ++- src/plugins/owner/MultiOwnerPlugin.sol | 3 ++ src/plugins/session/ISessionKeyPlugin.sol | 1 + src/plugins/session/SessionKeyPlugin.sol | 13 ++++-- .../SessionKeyPluginWithMultiOwner.t.sol | 41 ++++++++++++++++++- .../SessionKeyNativeTokenSpendLimits.t.sol | 2 +- .../SessionKeyPermissionsPlugin.t.sol | 3 +- 7 files changed, 59 insertions(+), 9 deletions(-) diff --git a/src/account/AccountExecutor.sol b/src/account/AccountExecutor.sol index 6e29ee64..ebae8704 100644 --- a/src/account/AccountExecutor.sol +++ b/src/account/AccountExecutor.sol @@ -92,8 +92,9 @@ abstract contract AccountExecutor { ) ) case 0 { - // If the call failed or did not return enough data, we return 1 (SIG_FAIL) as the validation data - validationData := 1 + // Bubble up the revert if the call reverts. + returndatacopy(0, 0, returndatasize()) + revert(0, returndatasize()) } default { // Otherwise, we return the first word of the return data as the validation data diff --git a/src/plugins/owner/MultiOwnerPlugin.sol b/src/plugins/owner/MultiOwnerPlugin.sol index 4d1a9807..9d8c2ada 100644 --- a/src/plugins/owner/MultiOwnerPlugin.sol +++ b/src/plugins/owner/MultiOwnerPlugin.sol @@ -204,6 +204,9 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { } /// @inheritdoc BasePlugin + /// @dev Since owner can be an ERC-1271 compliant contract, we won't know the format of the signatures. + /// Therefore, any invalid signature are treated as mismatched signatures in the ERC-4337 context unless + /// reverted in ERC-1271 owner signature validation. function userOpValidationFunction(uint8 functionId, UserOperation calldata userOp, bytes32 userOpHash) external view diff --git a/src/plugins/session/ISessionKeyPlugin.sol b/src/plugins/session/ISessionKeyPlugin.sol index 6f71cf95..5bf38808 100644 --- a/src/plugins/session/ISessionKeyPlugin.sol +++ b/src/plugins/session/ISessionKeyPlugin.sol @@ -13,6 +13,7 @@ interface ISessionKeyPlugin { error NotAuthorized(address caller); error SessionKeyNotFound(address sessionKey); error UnableToRemove(address sessionKey); + error InvalidSignature(address sessionKey); struct SessionKeyToRemove { address sessionKey; diff --git a/src/plugins/session/SessionKeyPlugin.sol b/src/plugins/session/SessionKeyPlugin.sol index dfaa8803..23c26167 100644 --- a/src/plugins/session/SessionKeyPlugin.sol +++ b/src/plugins/session/SessionKeyPlugin.sol @@ -168,13 +168,18 @@ contract SessionKeyPlugin is BasePlugin, ISessionKeyPlugin { (, address sessionKey) = abi.decode(userOp.callData[4:], (Call[], address)); bytes32 hash = userOpHash.toEthSignedMessageHash(); - if (_sessionKeys.contains(msg.sender, CastLib.toSetValue(sessionKey))) { - (address recoveredSig, ECDSA.RecoverError err) = hash.tryRecover(userOp.signature); - if (err == ECDSA.RecoverError.NoError && sessionKey == recoveredSig) { + (address recoveredSig, ECDSA.RecoverError err) = hash.tryRecover(userOp.signature); + if (err == ECDSA.RecoverError.NoError) { + if ( + _sessionKeys.contains(msg.sender, CastLib.toSetValue(sessionKey)) && sessionKey == recoveredSig + ) { return _SIG_VALIDATION_PASSED; + } else { + return _SIG_VALIDATION_FAILED; } + } else { + revert InvalidSignature(sessionKey); } - return _SIG_VALIDATION_FAILED; } revert NotImplemented(); } diff --git a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol index 98ba248e..fd7a055f 100644 --- a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol +++ b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol @@ -235,7 +235,7 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { assertEq(result, 0); } - function testFuzz_sessionKey_userOpValidation_invalid(uint8 sessionKeysSeed, uint64 signerSeed) public { + function testFuzz_sessionKey_userOpValidation_mismathcedSig(uint8 sessionKeysSeed, uint64 signerSeed) public { _createSessionKeys(sessionKeysSeed); (address signer, uint256 signerPrivate) = @@ -276,6 +276,45 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { assertEq(result, 1); } + function testFuzz_sessionKey_userOpValidation_invalidSig(uint8 sessionKeysSeed, uint64 signerSeed) public { + _createSessionKeys(sessionKeysSeed); + + (address signer, uint256 signerPrivate) = + makeAddrAndKey(string.concat("Signer", vm.toString(uint32(signerSeed)))); + + // The signer should not be a session key of the plugin - this is exceedingly unlikely but checking + // anyways. + vm.assume(!sessionKeyPlugin.isSessionKeyOf(address(account1), signer)); + + // Construct a user op to validate against + Call[] memory calls = new Call[](1); + calls[0] = Call({target: recipient, value: 1 wei, data: ""}); + UserOperation memory userOp = UserOperation({ + sender: address(account1), + nonce: 0, + initCode: "", + callData: abi.encodeCall( + ISessionKeyPlugin(address(sessionKeyPlugin)).executeWithSessionKey, (calls, signer) + ), + callGasLimit: CALL_GAS_LIMIT, + verificationGasLimit: VERIFICATION_GAS_LIMIT, + preVerificationGas: 0, + maxFeePerGas: 2, + maxPriorityFeePerGas: 1, + paymasterAndData: "", + signature: "" + }); + + bytes32 userOpHash = entryPoint.getUserOpHash(userOp); + userOp.signature = ""; + + vm.prank(address(account1)); + vm.expectRevert(abi.encodeWithSelector(ISessionKeyPlugin.InvalidSignature.selector, signer)); + uint256 result = sessionKeyPlugin.userOpValidationFunction( + uint8(ISessionKeyPlugin.FunctionId.USER_OP_VALIDATION_SESSION_KEY), userOp, userOpHash + ); + } + function testFuzz_sessionKey_invalidFunctionId(uint8 functionId, UserOperation memory userOp) public { vm.assume(functionId != uint8(ISessionKeyPlugin.FunctionId.USER_OP_VALIDATION_SESSION_KEY)); diff --git a/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol b/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol index 563d57dc..66986459 100644 --- a/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol @@ -291,7 +291,7 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { _runSessionKeyUserOp( calls, sessionKey1Private, - abi.encodeWithSelector(IEntryPoint.FailedOp.selector, 0, "AA24 signature error") + abi.encodeWithSelector(IEntryPoint.FailedOp.selector, 0, "AA23 reverted (or OOG)") ); } diff --git a/test/plugin/session/permissions/SessionKeyPermissionsPlugin.t.sol b/test/plugin/session/permissions/SessionKeyPermissionsPlugin.t.sol index 33ce259d..b1d82e9e 100644 --- a/test/plugin/session/permissions/SessionKeyPermissionsPlugin.t.sol +++ b/test/plugin/session/permissions/SessionKeyPermissionsPlugin.t.sol @@ -414,7 +414,8 @@ contract SessionKeyPermissionsPluginTest is Test { sessionKey1Private, abi.encodeCall(Counter.increment, ()), 0 wei, - abi.encodeWithSelector(IEntryPoint.FailedOp.selector, 0, "AA24 signature error") + // Entrypoint wont recognize and bubble up KeyNotRegistered custom error in ver0.6.0 + abi.encodeWithSelector(IEntryPoint.FailedOp.selector, 0, "AA23 reverted (or OOG)") ); // Attempting to use the new key should succeed From 1944ca9dbf31bf10819b8097526622a23997ac9d Mon Sep 17 00:00:00 2001 From: David Philipson Date: Wed, 10 Jan 2024 15:09:25 -0500 Subject: [PATCH 036/106] fix: [spearbit-29] remove redundant SSTOREs (#51) --- src/libraries/AssociatedLinkedListSetLib.sol | 3 --- src/libraries/LinkedListSetLib.sol | 2 -- 2 files changed, 5 deletions(-) diff --git a/src/libraries/AssociatedLinkedListSetLib.sol b/src/libraries/AssociatedLinkedListSetLib.sol index 51a0c781..123235cb 100644 --- a/src/libraries/AssociatedLinkedListSetLib.sol +++ b/src/libraries/AssociatedLinkedListSetLib.sol @@ -188,9 +188,6 @@ library AssociatedLinkedListSetLib { _store(cursorSlot, bytes32(0)); cursor = next; } while (!isSentinel(cursor) && cursor != bytes32(0)); - - StoragePointer sentinelSlot = _mapLookup(keyBuffer, SENTINEL_VALUE); - _store(sentinelSlot, bytes32(0)); } /// @notice Set the flags on a value in the set. diff --git a/src/libraries/LinkedListSetLib.sol b/src/libraries/LinkedListSetLib.sol index 284021f4..e2b7ed8f 100644 --- a/src/libraries/LinkedListSetLib.sol +++ b/src/libraries/LinkedListSetLib.sol @@ -124,8 +124,6 @@ library LinkedListSetLib { map[cursor] = bytes32(0); cursor = next; } while (!isSentinel(cursor) && cursor != bytes32(0)); - - map[SENTINEL_VALUE] = bytes32(0); } /// @notice Set the flags on a value in the set. From 18d8ace3d670839b80fc542c451d8992af9d3222 Mon Sep 17 00:00:00 2001 From: David Philipson Date: Wed, 10 Jan 2024 15:10:10 -0500 Subject: [PATCH 037/106] fix: [spearbit-43] simplify `tryRemoveKnown()` (#52) --- src/libraries/AssociatedLinkedListSetLib.sol | 4 ++-- src/libraries/LinkedListSetLib.sol | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/AssociatedLinkedListSetLib.sol b/src/libraries/AssociatedLinkedListSetLib.sol index 123235cb..e2793f76 100644 --- a/src/libraries/AssociatedLinkedListSetLib.sol +++ b/src/libraries/AssociatedLinkedListSetLib.sol @@ -164,9 +164,9 @@ library AssociatedLinkedListSetLib { } // Need to do: - // map[prev] = clearFlags(next) | getUserFlags(currentValue) | (next & HAS_NEXT_FLAG); + // map[prev] = clearUserFlags(next) | getUserFlags(currentValue); // map[unwrappedKey] = bytes32(0); - _store(prevSlot, clearFlags(next) | getUserFlags(currentValue) | (next & HAS_NEXT_FLAG)); + _store(prevSlot, clearUserFlags(next) | getUserFlags(currentValue)); _store(valueSlot, bytes32(0)); return true; diff --git a/src/libraries/LinkedListSetLib.sol b/src/libraries/LinkedListSetLib.sol index e2b7ed8f..cc87f85c 100644 --- a/src/libraries/LinkedListSetLib.sol +++ b/src/libraries/LinkedListSetLib.sol @@ -107,7 +107,7 @@ library LinkedListSetLib { return false; } - map[prev] = clearFlags(next) | getUserFlags(currentValue) | (next & HAS_NEXT_FLAG); + map[prev] = clearUserFlags(next) | getUserFlags(currentValue); map[unwrappedKey] = bytes32(0); return true; } From 1675a376400375755438b7f3ec567034e5ec990d Mon Sep 17 00:00:00 2001 From: David Philipson Date: Wed, 10 Jan 2024 15:10:58 -0500 Subject: [PATCH 038/106] fix: [spearbit-27] Remove redundant selector length check (#53) --- src/account/UpgradeableModularAccount.sol | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index e3712b3d..2cc5842d 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -187,9 +187,6 @@ contract UpgradeableModularAccount is bool hasPreValidationHooks; - if (userOp.callData.length < 4) { - revert UnrecognizedFunction(bytes4(userOp.callData)); - } bytes4 selector = _selectorFromCallData(userOp.callData); SelectorData storage selectorData = _getAccountStorage().selectorData[selector]; From e39b021634f3e2ffcc1f891ba0c19fa21a7cb981 Mon Sep 17 00:00:00 2001 From: David Philipson Date: Wed, 10 Jan 2024 15:11:43 -0500 Subject: [PATCH 039/106] fix: [spearbit-31] Remove unnecessary assignment (#54) --- src/libraries/AssociatedLinkedListSetLib.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libraries/AssociatedLinkedListSetLib.sol b/src/libraries/AssociatedLinkedListSetLib.sol index e2793f76..190222be 100644 --- a/src/libraries/AssociatedLinkedListSetLib.sol +++ b/src/libraries/AssociatedLinkedListSetLib.sol @@ -425,8 +425,7 @@ library AssociatedLinkedListSetLib { function _mapLookup(TempBytesMemory keyBuffer, bytes32 value) private pure returns (StoragePointer slot) { assembly ("memory-safe") { // Store the value in the last word. - let keyWord2 := value - mstore(add(keyBuffer, 0x60), keyWord2) + mstore(add(keyBuffer, 0x60), value) slot := keccak256(keyBuffer, 0x80) } } From a4f1fe4a941b5519e8c92e1ab8654bbdfb5cb46f Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Wed, 10 Jan 2024 18:16:11 -0500 Subject: [PATCH 040/106] chore: remove pinned foundry version (#59) --- .github/workflows/test.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 853af473..32bfbb1c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,8 +19,6 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 - with: - version: nightly-02292f2d2caa547968bd039c06dc53d98b72bf39 - name: "Install Pnpm" uses: "pnpm/action-setup@v2" @@ -71,8 +69,6 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 - with: - version: nightly-02292f2d2caa547968bd039c06dc53d98b72bf39 - name: Install forge dependencies run: forge install @@ -94,8 +90,6 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 - with: - version: nightly-02292f2d2caa547968bd039c06dc53d98b72bf39 - name: Install forge dependencies run: forge install From 3c1b5b6002be43e42c9c4feba182a83aaa2d139a Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Wed, 10 Jan 2024 16:00:26 -0800 Subject: [PATCH 041/106] fix: [spearbit-83] [quantstamp-12] revert in session key permissions preExecHook when function Id not matched (#47) --- .../session/permissions/SessionKeyPermissionsPlugin.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol b/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol index cb1d8a09..4f01329c 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol @@ -118,8 +118,9 @@ contract SessionKeyPermissionsPlugin is ISessionKeyPermissionsPlugin, SessionKey { if (functionId == uint8(FunctionId.PRE_EXECUTION_HOOK_UPDATE_LIMITS)) { _updateLimitsPreExec(msg.sender, data); + } else { + revert NotImplemented(); } - return ""; } /// @inheritdoc BasePlugin From 4629e8ddddf7c781ddda81e92108ec12f0de2aff Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Wed, 10 Jan 2024 16:00:51 -0800 Subject: [PATCH 042/106] fix: [spearbit-42] reorder add and remove owner s logic in multi owner plugin to save gas (#50) --- src/plugins/owner/MultiOwnerPlugin.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/owner/MultiOwnerPlugin.sol b/src/plugins/owner/MultiOwnerPlugin.sol index 9d8c2ada..b6d63f95 100644 --- a/src/plugins/owner/MultiOwnerPlugin.sol +++ b/src/plugins/owner/MultiOwnerPlugin.sol @@ -78,12 +78,13 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ /// @inheritdoc IMultiOwnerPlugin + /// @dev If an owner is present in both ownersToAdd and ownersToRemove, it will be added as owner function updateOwners(address[] memory ownersToAdd, address[] memory ownersToRemove) public isInitialized(msg.sender) { - _addOwnersOrRevert(_owners, msg.sender, ownersToAdd); _removeOwnersOrRevert(_owners, msg.sender, ownersToRemove); + _addOwnersOrRevert(_owners, msg.sender, ownersToAdd); if (_owners.isEmpty(msg.sender)) { revert EmptyOwnersNotAllowed(); From 436113e0c613f6aca705cd3aefef8c97b678baa5 Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Wed, 10 Jan 2024 16:28:04 -0800 Subject: [PATCH 043/106] fix: [spearbit-107] [quantstamp-22] use memory safe revert instead in AccountExecutor (#57) --- src/account/AccountExecutor.sol | 5 +++-- test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol | 5 ++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/account/AccountExecutor.sol b/src/account/AccountExecutor.sol index ebae8704..7b5c01d5 100644 --- a/src/account/AccountExecutor.sol +++ b/src/account/AccountExecutor.sol @@ -93,8 +93,9 @@ abstract contract AccountExecutor { ) case 0 { // Bubble up the revert if the call reverts. - returndatacopy(0, 0, returndatasize()) - revert(0, returndatasize()) + let m := mload(0x40) + returndatacopy(m, 0x00, returndatasize()) + revert(m, returndatasize()) } default { // Otherwise, we return the first word of the return data as the validation data diff --git a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol index fd7a055f..1e5e7f2e 100644 --- a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol +++ b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol @@ -279,8 +279,7 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { function testFuzz_sessionKey_userOpValidation_invalidSig(uint8 sessionKeysSeed, uint64 signerSeed) public { _createSessionKeys(sessionKeysSeed); - (address signer, uint256 signerPrivate) = - makeAddrAndKey(string.concat("Signer", vm.toString(uint32(signerSeed)))); + (address signer,) = makeAddrAndKey(string.concat("Signer", vm.toString(uint32(signerSeed)))); // The signer should not be a session key of the plugin - this is exceedingly unlikely but checking // anyways. @@ -310,7 +309,7 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { vm.prank(address(account1)); vm.expectRevert(abi.encodeWithSelector(ISessionKeyPlugin.InvalidSignature.selector, signer)); - uint256 result = sessionKeyPlugin.userOpValidationFunction( + sessionKeyPlugin.userOpValidationFunction( uint8(ISessionKeyPlugin.FunctionId.USER_OP_VALIDATION_SESSION_KEY), userOp, userOpHash ); } From 540b0e3884d1f3a315c4f9046c3af25b5d21fd76 Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Wed, 10 Jan 2024 16:28:24 -0800 Subject: [PATCH 044/106] fix: [quantstamp-15] only support executeWithSessionKey method in session key permission hooks (#48) --- .../session/permissions/SessionKeyPermissionsPlugin.sol | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol b/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol index 4f01329c..b20474f7 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol @@ -248,6 +248,9 @@ contract SessionKeyPermissionsPlugin is ISessionKeyPermissionsPlugin, SessionKey /// @dev A pre user op validation hook that checks the permissions of the key used to validate the user op. function _checkUserOpPermissions(UserOperation calldata userOp) internal returns (uint256) { + // If not calling executeWithSessionKey, nothing to do. Return 0 as validation success. + if (bytes4(userOp.callData) != ISessionKeyPlugin.executeWithSessionKey.selector) return 0; + // Decode the executions array and the session key from the user op's calldata (Call[] memory calls, address sessionKey) = abi.decode(userOp.callData[4:], (Call[], address)); @@ -365,6 +368,9 @@ contract SessionKeyPermissionsPlugin is ISessionKeyPermissionsPlugin, SessionKey /// @dev Runs as a pre exec hook, and updates the spend limits of the session key in use function _updateLimitsPreExec(address account, bytes calldata callData) internal { + // If not calling executeWithSessionKey, nothing to do. + if (bytes4(callData) != ISessionKeyPlugin.executeWithSessionKey.selector) return; + (Call[] memory calls, address sessionKey) = abi.decode(callData[4:], (Call[], address)); uint256 callsLength = calls.length; From 045134854438033963b71301a7a3fea7aa76ec34 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Thu, 11 Jan 2024 17:29:34 -0500 Subject: [PATCH 045/106] fix: [spearbit-44] optimize CountableLinkedListSetLib (#56) --- src/libraries/CountableLinkedListSetLib.sol | 26 +++++++++------------ 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/libraries/CountableLinkedListSetLib.sol b/src/libraries/CountableLinkedListSetLib.sol index 9a8cd84d..e1e7bb89 100644 --- a/src/libraries/CountableLinkedListSetLib.sol +++ b/src/libraries/CountableLinkedListSetLib.sol @@ -7,14 +7,12 @@ import {SetValue} from "./LinkedListSetUtils.sol"; /// @title Countable Linked List Set Library /// @author Alchemy /// @notice This library adds the ability to count the number of occurrences of a value in a linked list set. +/// @dev The counter is stored in the upper 8 bits of the the flag bytes, so the maximum value of the counter +/// is 255. This means each value can be included a maximum of 256 times in the set, as the counter is 0 when +/// the value is first added. library CountableLinkedListSetLib { using LinkedListSetLib for LinkedListSet; - /// @dev The counter is stored in the upper 8 bits of the the flag bytes, so the maximum value of the counter - /// is 255. This means each value can be included a maximum of 256 times in the set, as the counter is 0 when - /// the value is first added. - uint16 internal constant _MAX_COUNTER_VALUE = 255; - /// @notice Increment an existing value in the set, or add it if it doesn't exist. /// @dev The counter is stored in the upper 8 bits of the the flag bytes. Because this library repurposes a /// portion of the flag bytes to store the counter, it's important to not use the upper 8 bits to store flags. @@ -27,15 +25,14 @@ library CountableLinkedListSetLib { return set.tryAdd(value); } uint16 flags = set.getFlags(value); - // Use the upper 8 bits of the (16-bit) flag for the counter. - uint16 counter = flags >> 8; - if (counter == _MAX_COUNTER_VALUE) { + if (flags > 0xFEFF) { + // The counter is at its maximum value, so don't increment it. return false; } unchecked { - ++counter; + flags += 0x100; } - return set.trySetFlags(value, (counter << 8) | (flags & 0xFF)); + return set.trySetFlags(value, flags); } /// @notice Decrement an existing value in the set, or remove it if the count has reached 0. @@ -50,15 +47,14 @@ library CountableLinkedListSetLib { return false; } uint16 flags = set.getFlags(value); - // Use the upper 8 bits of the (16-bit) flag for the counter. - uint16 counter = flags >> 8; - if (counter == 0) { + if (flags < 0x100) { + // The counter is 0, so remove the value. return set.tryRemove(value); } unchecked { - --counter; + flags -= 0x100; } - return set.trySetFlags(value, (counter << 8) | (flags & 0xFF)); + return set.trySetFlags(value, flags); } /// @notice Get the number of occurrences of a value in the set. From 152910a4ae32ab38b6747dab4291a23f92d9a91a Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Fri, 12 Jan 2024 10:19:03 -0800 Subject: [PATCH 046/106] fix: [spearbit-14] remove unused error in PluginManagerInternals (#61) --- src/account/PluginManagerInternals.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/src/account/PluginManagerInternals.sol b/src/account/PluginManagerInternals.sol index c4a4a8a8..a4791da9 100644 --- a/src/account/PluginManagerInternals.sol +++ b/src/account/PluginManagerInternals.sol @@ -49,7 +49,6 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { error DuplicatePreUserOpValidationHookLimitExceeded(bytes4 selector, FunctionReference hook); error Erc4337FunctionNotAllowed(bytes4 selector); error ExecutionFunctionAlreadySet(bytes4 selector); - error ExecutionFunctionNotSet(bytes4 selector); error IPluginFunctionNotAllowed(bytes4 selector); error IPluginInterfaceNotAllowed(); error InvalidDependenciesProvided(); From fe87e379d0d02907d2f3b4ddc403b37cc8a4e7a5 Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Fri, 12 Jan 2024 10:24:50 -0800 Subject: [PATCH 047/106] fix: [spearbit-30] [quantstamp-26] fix AssociatedLinkedListSetLib _ASSOCIATED_STORAGE_PREFIX bytes4 (#60) --- src/libraries/AssociatedLinkedListSetLib.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/AssociatedLinkedListSetLib.sol b/src/libraries/AssociatedLinkedListSetLib.sol index 190222be..b9bfade0 100644 --- a/src/libraries/AssociatedLinkedListSetLib.sol +++ b/src/libraries/AssociatedLinkedListSetLib.sol @@ -25,7 +25,7 @@ library AssociatedLinkedListSetLib { // Mapping keys exclude the upper 15 bits of the meta bytes, which allows keys to be either a value or the // sentinel. - bytes4 internal constant _ASSOCIATED_STORAGE_PREFIX = 0x9cc6c923; // bytes4(keccak256("AssociatedLinkedListSet")) + bytes4 internal constant _ASSOCIATED_STORAGE_PREFIX = 0xf938c976; // bytes4(keccak256("AssociatedLinkedListSet")) // A custom type representing the index of a storage slot type StoragePointer is bytes32; From b9df2828ec124969e20d8a7728579d7d8795b19e Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Fri, 12 Jan 2024 11:48:19 -0800 Subject: [PATCH 048/106] fix: [quantstamp-32] add owner sorting check and comments in factory and MultiOwnerPlugin for different order of owners (#49) --- src/factory/MultiOwnerMSCAFactory.sol | 44 +++++-------------- .../MultiOwnerTokenReceiverMSCAFactory.sol | 43 +++++------------- src/helpers/FactoryHelpers.sol | 30 +++++++++++++ src/plugins/owner/MultiOwnerPlugin.sol | 5 ++- test/factory/MultiOwnerMSCAFactoryTest.t.sol | 4 +- test/plugin/owner/MultiOwnerPlugin.t.sol | 22 ++++++++-- .../owner/MultiOwnerPluginIntegration.t.sol | 4 +- test/upgrade/MSCAToMSCA.t.sol | 2 +- 8 files changed, 82 insertions(+), 72 deletions(-) create mode 100644 src/helpers/FactoryHelpers.sol diff --git a/src/factory/MultiOwnerMSCAFactory.sol b/src/factory/MultiOwnerMSCAFactory.sol index e4e7a7e6..b05329d7 100644 --- a/src/factory/MultiOwnerMSCAFactory.sol +++ b/src/factory/MultiOwnerMSCAFactory.sol @@ -9,6 +9,7 @@ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol import {IAccountInitializable} from "../interfaces/IAccountInitializable.sol"; import {IEntryPoint} from "../interfaces/erc4337/IEntryPoint.sol"; +import {FactoryHelpers} from "../helpers/FactoryHelpers.sol"; /// @title Multi Owner Plugin MSCA (Modular Smart Contract Account) Factory /// @author Alchemy @@ -22,8 +23,7 @@ contract MultiOwnerMSCAFactory is Ownable2Step { IEntryPoint public immutable ENTRYPOINT; error OwnersArrayEmpty(); - error ZeroAddressOwner(); - error DuplicateOwner(); + error InvalidOwner(); error TransferFailed(); /// @notice Constructor for the factory @@ -46,14 +46,18 @@ contract MultiOwnerMSCAFactory is Ownable2Step { /// @notice Create a modular smart contract account /// @dev Account address depends on salt, impl addr, plugins and plugin init data - /// @dev Owner array must be valid else the plugin installation step would revert. See getAddress below + /// @dev The owner array must be in strictly ascending order and not include the 0 address. /// @param salt salt for additional entropy for create2 /// @param owners address array of the owners function createAccount(uint256 salt, address[] calldata owners) external returns (address addr) { + if (!FactoryHelpers.isValidOwnerArray(owners)) { + revert InvalidOwner(); + } + bytes[] memory pluginInitBytes = new bytes[](1); pluginInitBytes[0] = abi.encode(owners); - bytes32 combinedSalt = _getSalt(salt, pluginInitBytes[0]); + bytes32 combinedSalt = FactoryHelpers.getCombinedSalt(salt, pluginInitBytes[0]); addr = Create2.computeAddress( combinedSalt, keccak256(abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(IMPL, ""))) ); @@ -111,8 +115,7 @@ contract MultiOwnerMSCAFactory is Ownable2Step { } /// @notice Getter for counterfactual address based on input params - /// @dev Owner array cannot be empty, cannot contain address(0), and cannot contain duplicates - /// @param salt salt for additional entropy for create2 + /// @dev The owner array must be in strictly ascending order and not include the 0 address. /// @param owners array of addresses of the owner function getAddress(uint256 salt, address[] calldata owners) external view returns (address) { // array can't be empty @@ -120,36 +123,13 @@ contract MultiOwnerMSCAFactory is Ownable2Step { revert OwnersArrayEmpty(); } - for (uint256 i = 0; i < owners.length;) { - // array can't contain address(0) - if (owners[i] == address(0)) { - revert ZeroAddressOwner(); - } - - for (uint256 j = i + 1; j < owners.length;) { - // array can't have matching elements - if (owners[i] == owners[j]) { - revert DuplicateOwner(); - } - unchecked { - ++j; - } - } - unchecked { - ++i; - } + if (!FactoryHelpers.isValidOwnerArray(owners)) { + revert InvalidOwner(); } return Create2.computeAddress( - _getSalt(salt, abi.encode(owners)), + FactoryHelpers.getCombinedSalt(salt, abi.encode(owners)), keccak256(abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(IMPL, ""))) ); } - - /// @notice Gets this factory's create2 salt based on the input params - /// @param salt additional entropy for create2 - /// @param owners encoded bytes array of owner addresses - function _getSalt(uint256 salt, bytes memory owners) internal pure returns (bytes32) { - return keccak256(abi.encode(salt, owners)); - } } diff --git a/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol b/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol index 7cd6fa14..b590a9ad 100644 --- a/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol +++ b/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol @@ -9,6 +9,7 @@ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol import {IAccountInitializable} from "../interfaces/IAccountInitializable.sol"; import {IEntryPoint} from "../interfaces/erc4337/IEntryPoint.sol"; +import {FactoryHelpers} from "../helpers/FactoryHelpers.sol"; /// @title Multi Owner Plugin + Token Receiver MSCA (Modular Smart Contract Account) Factory /// @author Alchemy @@ -24,8 +25,7 @@ contract MultiOwnerTokenReceiverMSCAFactory is Ownable2Step { IEntryPoint public immutable ENTRYPOINT; error OwnersArrayEmpty(); - error ZeroAddressOwner(); - error DuplicateOwner(); + error InvalidOwner(); error TransferFailed(); /// @notice Constructor for the factory @@ -52,14 +52,18 @@ contract MultiOwnerTokenReceiverMSCAFactory is Ownable2Step { /// @notice Create a modular smart contract account /// @dev Account address depends on salt, impl addr, plugins and plugin init data - /// @dev Owner array must be valid. See getAddress below + /// @dev The owner array must be in strictly ascending order and not include the 0 address. /// @param salt salt for additional entropy for create2 /// @param owners address array of the owners function createAccount(uint256 salt, address[] calldata owners) external returns (address addr) { + if (!FactoryHelpers.isValidOwnerArray(owners)) { + revert InvalidOwner(); + } + bytes[] memory pluginInitBytes = new bytes[](2); // empty bytes for TokenReceiverPlugin init pluginInitBytes[0] = abi.encode(owners); - bytes32 combinedSalt = _getSalt(salt, pluginInitBytes[0]); + bytes32 combinedSalt = FactoryHelpers.getCombinedSalt(salt, pluginInitBytes[0]); addr = Create2.computeAddress( combinedSalt, keccak256(abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(IMPL, ""))) ); @@ -119,7 +123,7 @@ contract MultiOwnerTokenReceiverMSCAFactory is Ownable2Step { } /// @notice Getter for counterfactual address based on input params - /// @dev Owner array cannot be empty, cannot contain address(0), and cannot contain duplicates + /// @dev The owner array must be in strictly ascending order and not include the 0 address. /// @param salt salt for additional entropy for create2 /// @param owners array of addresses of the owner function getAddress(uint256 salt, address[] calldata owners) external view returns (address) { @@ -128,36 +132,13 @@ contract MultiOwnerTokenReceiverMSCAFactory is Ownable2Step { revert OwnersArrayEmpty(); } - for (uint256 i = 0; i < owners.length;) { - // array can't contain address(0) - if (owners[i] == address(0)) { - revert ZeroAddressOwner(); - } - - for (uint256 j = i + 1; j < owners.length;) { - // array can't have matching elements - if (owners[i] == owners[j]) { - revert DuplicateOwner(); - } - unchecked { - ++j; - } - } - unchecked { - ++i; - } + if (!FactoryHelpers.isValidOwnerArray(owners)) { + revert InvalidOwner(); } return Create2.computeAddress( - _getSalt(salt, abi.encode(owners)), + FactoryHelpers.getCombinedSalt(salt, abi.encode(owners)), keccak256(abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(IMPL, ""))) ); } - - /// @notice Gets this factory's create2 salt based on the input params - /// @param salt additional entropy for create2 - /// @param owners encoded bytes array of owner addresses - function _getSalt(uint256 salt, bytes memory owners) internal pure returns (bytes32) { - return keccak256(abi.encode(salt, owners)); - } } diff --git a/src/helpers/FactoryHelpers.sol b/src/helpers/FactoryHelpers.sol new file mode 100644 index 00000000..ce6f4ed2 --- /dev/null +++ b/src/helpers/FactoryHelpers.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.21; + +library FactoryHelpers { + /// @dev The owner array must be in strictly ascending order and not include the 0 address. + /// @param owners encoded bytes array of owner addresses + /// @return bool if the owners array is valid + function isValidOwnerArray(address[] calldata owners) internal pure returns (bool) { + address currentOwnerValue; + for (uint256 i = 0; i < owners.length;) { + if (owners[i] <= currentOwnerValue) { + return false; + } + currentOwnerValue = owners[i]; + + unchecked { + ++i; + } + } + return true; + } + + /// @notice Gets this factory's create2 salt based on the input params + /// @param salt additional entropy for create2 + /// @param owners encoded bytes array of owner addresses + /// @return combinedSalt of salt and owners + function getCombinedSalt(uint256 salt, bytes memory owners) internal pure returns (bytes32) { + return keccak256(abi.encode(salt, owners)); + } +} diff --git a/src/plugins/owner/MultiOwnerPlugin.sol b/src/plugins/owner/MultiOwnerPlugin.sol index b6d63f95..4a4a3308 100644 --- a/src/plugins/owner/MultiOwnerPlugin.sol +++ b/src/plugins/owner/MultiOwnerPlugin.sol @@ -78,7 +78,8 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ /// @inheritdoc IMultiOwnerPlugin - /// @dev If an owner is present in both ownersToAdd and ownersToRemove, it will be added as owner + /// @dev If an owner is present in both ownersToAdd and ownersToRemove, it will be added as owner. + /// The owner array cannot have 0 or duplicated addresses. function updateOwners(address[] memory ownersToAdd, address[] memory ownersToRemove) public isInitialized(msg.sender) @@ -191,6 +192,7 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ /// @inheritdoc BasePlugin + /// @dev The owner array cannot have 0 or duplicated addresses. function _onInstall(bytes calldata data) internal override isNotInitialized(msg.sender) { (address[] memory initialOwners) = abi.decode(data, (address[])); if (initialOwners.length == 0) { @@ -392,6 +394,7 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { ) private { uint256 length = ownersToAdd.length; for (uint256 i = 0; i < length;) { + // Catches address(0), duplicated addresses if (!ownerSet.tryAdd(associated, CastLib.toSetValue(ownersToAdd[i]))) { revert InvalidOwner(ownersToAdd[i]); } diff --git a/test/factory/MultiOwnerMSCAFactoryTest.t.sol b/test/factory/MultiOwnerMSCAFactoryTest.t.sol index 67be8a50..6fa78928 100644 --- a/test/factory/MultiOwnerMSCAFactoryTest.t.sol +++ b/test/factory/MultiOwnerMSCAFactoryTest.t.sol @@ -89,13 +89,13 @@ contract MultiOwnerMSCAFactoryTest is Test { address[] memory badOwners = new address[](2); - vm.expectRevert(MultiOwnerMSCAFactory.ZeroAddressOwner.selector); + vm.expectRevert(MultiOwnerMSCAFactory.InvalidOwner.selector); factory.getAddress(0, badOwners); badOwners[0] = address(1); badOwners[1] = address(1); - vm.expectRevert(MultiOwnerMSCAFactory.DuplicateOwner.selector); + vm.expectRevert(MultiOwnerMSCAFactory.InvalidOwner.selector); factory.getAddress(0, badOwners); } diff --git a/test/plugin/owner/MultiOwnerPlugin.t.sol b/test/plugin/owner/MultiOwnerPlugin.t.sol index b6a29593..ec1066e8 100644 --- a/test/plugin/owner/MultiOwnerPlugin.t.sol +++ b/test/plugin/owner/MultiOwnerPlugin.t.sol @@ -48,9 +48,9 @@ contract MultiOwnerPluginTest is Test { // set up owners for accountA ownerArray = new address[](3); - ownerArray[0] = owner1; - ownerArray[1] = owner2; - ownerArray[2] = owner3; + ownerArray[0] = owner2; + ownerArray[1] = owner3; + ownerArray[2] = owner1; vm.startPrank(accountA); plugin.onInstall(abi.encode(ownerArray)); @@ -115,6 +115,22 @@ contract MultiOwnerPluginTest is Test { plugin.updateOwners(new address[](0), ownerArray); } + function test_updateOwners_failWithZeroAddressOwner() public { + address[] memory ownersToAdd = new address[](2); + + vm.expectRevert(abi.encodeWithSelector(IMultiOwnerPlugin.InvalidOwner.selector, address(0))); + plugin.updateOwners(ownersToAdd, new address[](0)); + } + + function test_updateOwners_failWithDuplicatedAddresses() public { + address[] memory ownersToAdd = new address[](2); + ownersToAdd[0] = ownerofContractOwner; + ownersToAdd[1] = ownerofContractOwner; + + vm.expectRevert(abi.encodeWithSelector(IMultiOwnerPlugin.InvalidOwner.selector, ownerofContractOwner)); + plugin.updateOwners(ownersToAdd, new address[](0)); + } + function test_updateOwners_success() public { (address[] memory owners) = plugin.ownersOf(accountA); assertEq(owners, plugin.owners()); diff --git a/test/plugin/owner/MultiOwnerPluginIntegration.t.sol b/test/plugin/owner/MultiOwnerPluginIntegration.t.sol index 0cce360b..f3567e7d 100644 --- a/test/plugin/owner/MultiOwnerPluginIntegration.t.sol +++ b/test/plugin/owner/MultiOwnerPluginIntegration.t.sol @@ -68,8 +68,8 @@ contract MultiOwnerPluginIntegration is Test { // setup account with MultiOwnerMSCAFactory owners = new address[](2); - owners[0] = owner1; - owners[1] = owner2; + owners[0] = owner1 > owner2 ? owner2 : owner1; + owners[1] = owner2 > owner1 ? owner2 : owner1; account = UpgradeableModularAccount(payable(factory.getAddress(0, owners))); vm.deal(address(account), 100 ether); factory.createAccount(0, owners); diff --git a/test/upgrade/MSCAToMSCA.t.sol b/test/upgrade/MSCAToMSCA.t.sol index d4e9134e..395e1cda 100644 --- a/test/upgrade/MSCAToMSCA.t.sol +++ b/test/upgrade/MSCAToMSCA.t.sol @@ -31,8 +31,8 @@ contract MSCAToMSCATest is Test { event Upgraded(address indexed implementation); function setUp() public { - owners.push(makeAddr("owner1")); owners.push(makeAddr("owner2")); + owners.push(makeAddr("owner1")); entryPoint = IEntryPoint(address(new EntryPoint())); mscaImpl1 = address(new UpgradeableModularAccount(entryPoint)); mscaImpl2 = address(new UpgradeableModularAccount(entryPoint)); From ac9c87fd9dbe0803a6812fda757297809bda3bec Mon Sep 17 00:00:00 2001 From: David Philipson Date: Sat, 13 Jan 2024 23:19:08 -0500 Subject: [PATCH 049/106] fix: [spearbit-11] Clear bits more efficiently (#55) --- src/account/AccountExecutor.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/account/AccountExecutor.sol b/src/account/AccountExecutor.sol index 7b5c01d5..e76c5a28 100644 --- a/src/account/AccountExecutor.sol +++ b/src/account/AccountExecutor.sol @@ -123,9 +123,9 @@ abstract contract AccountExecutor { // mask out the fist 4 bytes, then OR in the new selector. let existingWord := mload(add(buffer, 0x20)) // Clear the upper 4 bytes of the existing word - existingWord := and(existingWord, shr(32, not(0))) + existingWord := shr(32, shl(32, existingWord)) // Clear the lower 28 bytes of the selector - pluginSelector := and(pluginSelector, shl(224, 0xFFFFFFFF)) + pluginSelector := shl(224, shr(224, pluginSelector)) // OR in the new selector existingWord := or(existingWord, pluginSelector) mstore(add(buffer, 0x20), existingWord) From d7ab6aefb1ee7cc33f0f545316b678a6351ca620 Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Tue, 16 Jan 2024 17:13:15 -0800 Subject: [PATCH 050/106] fix: [spearbit-96] make clearFlag consistent in clear() for associatedLinkedList (#69) --- src/libraries/AssociatedLinkedListSetLib.sol | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libraries/AssociatedLinkedListSetLib.sol b/src/libraries/AssociatedLinkedListSetLib.sol index b9bfade0..2e008e23 100644 --- a/src/libraries/AssociatedLinkedListSetLib.sol +++ b/src/libraries/AssociatedLinkedListSetLib.sol @@ -182,9 +182,8 @@ library AssociatedLinkedListSetLib { bytes32 cursor = SENTINEL_VALUE; do { - bytes32 cleared = clearFlags(cursor); - StoragePointer cursorSlot = _mapLookup(keyBuffer, cleared); - bytes32 next = _load(cursorSlot); + StoragePointer cursorSlot = _mapLookup(keyBuffer, cursor); + bytes32 next = clearFlags(_load(cursorSlot)); _store(cursorSlot, bytes32(0)); cursor = next; } while (!isSentinel(cursor) && cursor != bytes32(0)); From cac319deaa23bf0bc3303435f98f27656135f521 Mon Sep 17 00:00:00 2001 From: howy <132113803+howydev@users.noreply.github.com> Date: Wed, 17 Jan 2024 11:19:03 -0500 Subject: [PATCH 051/106] fix: [spearbit-47] add evm version=paris in config (#67) --- foundry.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/foundry.toml b/foundry.toml index ffb522b1..eb276488 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,5 +1,6 @@ [profile.default] solc = '0.8.21' +evm_version='paris' via_ir = true src = 'src' out = 'out' @@ -19,12 +20,14 @@ depth = 10 [profile.optimized-build] via_ir = true +evm_version='paris' script = 'src' test = 'src' out = 'out-optimized' [profile.lite] solc = '0.8.21' +evm_version='paris' via_ir = false optimizer = true optimizer_runs = 10_000 @@ -32,10 +35,12 @@ ignored_error_codes = [] [profile.deep.fuzz] runs = 10000 +evm_version='paris' [profile.deep.invariant] runs = 5000 depth = 32 +evm_version='paris' [fmt] line_length = 115 From 61ff1d3f7455d7bfd2b022d9640518d0938dcee3 Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Wed, 17 Jan 2024 11:39:23 -0800 Subject: [PATCH 052/106] fix: [spearbit-64] update comments in BasePlugin (#70) --- src/plugins/BasePlugin.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/BasePlugin.sol b/src/plugins/BasePlugin.sol index c9c100b4..c8ffc773 100644 --- a/src/plugins/BasePlugin.sol +++ b/src/plugins/BasePlugin.sol @@ -10,7 +10,7 @@ import {UserOperation} from "../interfaces/erc4337/UserOperation.sol"; /// @title Base contract for plugins /// @dev Implements ERC-165 to support IPlugin's interface, which is a requirement /// for plugin installation. This also ensures that plugin interactions cannot -/// happen via the standard execution funtions `execute` and `executeBatch`. +/// happen via the standard execution funtions `execute`, `executeBatch`, and `executeFromPluginExternal`. /// Note that the plugins implement BasePlugins cannot be installed when creating an account (aka installed in the /// account constructor) unless onInstall is overriden without checking codesize of caller (account). Checking /// codesize of account is to prevent EOA from accidentally calling plugin and initiate states which will make it @@ -200,8 +200,8 @@ abstract contract BasePlugin is ERC165, IPlugin { /// This function call must use less than 30 000 gas. /// /// Supporting the IPlugin interface is a requirement for plugin installation. This is also used - /// by the modular account to prevent standard execution functions `execute` and `executeBatch` from - /// making calls to plugins. + /// by the modular account to prevent standard execution functions `execute`, `executeBatch`, and + /// `executeFromPluginExternal` from making calls to plugins. /// @param interfaceId The interface ID to check for support. /// @return True if the contract supports `interfaceId`. function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { From 456d322ea94a096f031974a9d3f05bfb1d877ecb Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Wed, 17 Jan 2024 15:41:47 -0800 Subject: [PATCH 053/106] fix: remove unnecessary view functions for MultiOwnerPlugin (#68) --- src/plugins/owner/IMultiOwnerPlugin.sol | 13 --------- src/plugins/owner/MultiOwnerPlugin.sol | 28 +++---------------- test/plugin/owner/MultiOwnerPlugin.t.sol | 20 ++++--------- .../owner/MultiOwnerPluginIntegration.t.sol | 6 ++-- test/upgrade/MSCAToMSCA.t.sol | 4 ++- 5 files changed, 15 insertions(+), 56 deletions(-) diff --git a/src/plugins/owner/IMultiOwnerPlugin.sol b/src/plugins/owner/IMultiOwnerPlugin.sol index 4cdfe1d8..7c352b2a 100644 --- a/src/plugins/owner/IMultiOwnerPlugin.sol +++ b/src/plugins/owner/IMultiOwnerPlugin.sol @@ -28,19 +28,6 @@ interface IMultiOwnerPlugin { /// @param ownersToRemove The address array of owners to be removed. function updateOwners(address[] memory ownersToAdd, address[] memory ownersToRemove) external; - /// @notice Check if an address is an owner of the current account. - /// @dev This function is installed on the account as part of plugin installation, and should - /// only be called from an account. - /// @param ownerToCheck The owner to check if it is an owner of the current account. - /// @return True if the address is an owner of the account. - function isOwner(address ownerToCheck) external view returns (bool); - - /// @notice Get the owners of the current account. - /// @dev This function is installed on the account as part of plugin installation, and should - /// only be called from an account. - /// @return The addresses of the owners of the account. - function owners() external view returns (address[] memory); - /// @notice Gets the EIP712 domain /// @dev This implementation is different from typical 712 via its use of msg.sender instead. As such, it /// should only be called from the SCAs that has installed this. See ERC-5267. diff --git a/src/plugins/owner/MultiOwnerPlugin.sol b/src/plugins/owner/MultiOwnerPlugin.sol index 4a4a3308..d14f5226 100644 --- a/src/plugins/owner/MultiOwnerPlugin.sol +++ b/src/plugins/owner/MultiOwnerPlugin.sol @@ -98,16 +98,6 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { // ┃ Execution view functions ┃ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - /// @inheritdoc IMultiOwnerPlugin - function isOwner(address ownerToCheck) external view returns (bool) { - return isOwnerOf(msg.sender, ownerToCheck); - } - - /// @inheritdoc IMultiOwnerPlugin - function owners() external view returns (address[] memory) { - return ownersOf(msg.sender); - } - /// @inheritdoc IMultiOwnerPlugin function eip712Domain() public @@ -253,12 +243,10 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { function pluginManifest() external pure override returns (PluginManifest memory) { PluginManifest memory manifest; - manifest.executionFunctions = new bytes4[](5); + manifest.executionFunctions = new bytes4[](3); manifest.executionFunctions[0] = this.updateOwners.selector; - manifest.executionFunctions[1] = this.owners.selector; - manifest.executionFunctions[2] = this.isOwner.selector; - manifest.executionFunctions[3] = this.eip712Domain.selector; - manifest.executionFunctions[4] = this.isValidSignature.selector; + manifest.executionFunctions[1] = this.eip712Domain.selector; + manifest.executionFunctions[2] = this.isValidSignature.selector; ManifestFunction memory ownerUserOpValidationFunction = ManifestFunction({ functionType: ManifestAssociatedFunctionType.SELF, @@ -305,7 +293,7 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { }); // Update Modular Account's native functions to use runtimeValidationFunction provided by this plugin - manifest.runtimeValidationFunctions = new ManifestAssociatedFunction[](10); + manifest.runtimeValidationFunctions = new ManifestAssociatedFunction[](8); manifest.runtimeValidationFunctions[0] = ManifestAssociatedFunction({ executionSelector: this.updateOwners.selector, associatedFunction: ownerOrSelfRuntimeValidationFunction @@ -335,14 +323,6 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { associatedFunction: alwaysAllowFunction }); manifest.runtimeValidationFunctions[7] = ManifestAssociatedFunction({ - executionSelector: this.isOwner.selector, - associatedFunction: alwaysAllowFunction - }); - manifest.runtimeValidationFunctions[8] = ManifestAssociatedFunction({ - executionSelector: this.owners.selector, - associatedFunction: alwaysAllowFunction - }); - manifest.runtimeValidationFunctions[9] = ManifestAssociatedFunction({ executionSelector: this.eip712Domain.selector, associatedFunction: alwaysAllowFunction }); diff --git a/test/plugin/owner/MultiOwnerPlugin.t.sol b/test/plugin/owner/MultiOwnerPlugin.t.sol index ec1066e8..4f71a141 100644 --- a/test/plugin/owner/MultiOwnerPlugin.t.sol +++ b/test/plugin/owner/MultiOwnerPlugin.t.sol @@ -58,18 +58,17 @@ contract MultiOwnerPluginTest is Test { function test_pluginManifest() public { PluginManifest memory manifest = plugin.pluginManifest(); - // 5 execution functions - assertEq(5, manifest.executionFunctions.length); + // 3 execution functions + assertEq(3, manifest.executionFunctions.length); // 5 native + 1 plugin exec func assertEq(6, manifest.userOpValidationFunctions.length); - // 5 native + 1 plugin exec func + 4 plugin view func - assertEq(10, manifest.runtimeValidationFunctions.length); + // 5 native + 1 plugin exec func + 2 plugin view func + assertEq(8, manifest.runtimeValidationFunctions.length); } function test_onUninstall_success() public { plugin.onUninstall(abi.encode("")); address[] memory returnedOwners = plugin.ownersOf(accountA); - assertEq(returnedOwners, plugin.owners()); assertEq(0, returnedOwners.length); } @@ -80,7 +79,6 @@ contract MultiOwnerPluginTest is Test { vm.startPrank(address(contractOwner)); plugin.onInstall(abi.encode(owners)); address[] memory returnedOwners = plugin.ownersOf(address(contractOwner)); - assertEq(returnedOwners, plugin.owners()); assertEq(returnedOwners.length, 1); assertEq(returnedOwners[0], owner1); vm.stopPrank(); @@ -89,8 +87,6 @@ contract MultiOwnerPluginTest is Test { function test_eip712Domain() public { assertEq(true, plugin.isOwnerOf(accountA, owner2)); assertEq(false, plugin.isOwnerOf(accountA, address(contractOwner))); - assertEq(true, plugin.isOwner(owner2)); - assertEq(false, plugin.isOwner(address(contractOwner))); ( bytes1 fields, @@ -133,7 +129,6 @@ contract MultiOwnerPluginTest is Test { function test_updateOwners_success() public { (address[] memory owners) = plugin.ownersOf(accountA); - assertEq(owners, plugin.owners()); assertEq(Utils.reverseAddressArray(ownerArray), owners); // remove should also work @@ -144,7 +139,6 @@ contract MultiOwnerPluginTest is Test { plugin.updateOwners(new address[](0), ownersToRemove); (address[] memory newOwnerList) = plugin.ownersOf(accountA); - assertEq(newOwnerList, plugin.owners()); assertEq(newOwnerList.length, 1); assertEq(newOwnerList[0], owner3); } @@ -168,8 +162,6 @@ contract MultiOwnerPluginTest is Test { address[] memory ownersToAdd = new address[](1); ownersToAdd[0] = signer; - assertEq(plugin.isOwnerOf(accountA, signer), plugin.isOwner(signer)); - if (!plugin.isOwnerOf(accountA, signer)) { // sig check should fail assertEq(bytes4(0xFFFFFFFF), plugin.isValidSignature(digest, abi.encodePacked(r, s, v))); @@ -217,7 +209,7 @@ contract MultiOwnerPluginTest is Test { } function test_multiOwnerPlugin_sentinelIsNotOwner() public { - assertEq(false, plugin.isOwner(address(1))); + assertFalse(plugin.isOwnerOf(accountA, address(1))); } function testFuzz_userOpValidationFunction_ContractOwner(UserOperation memory userOp) public { @@ -277,8 +269,6 @@ contract MultiOwnerPluginTest is Test { address[] memory ownersToAdd = new address[](1); ownersToAdd[0] = signer; - assertEq(plugin.isOwnerOf(accountA, signer), plugin.isOwner(signer)); - // Only check that the signature should fail if the signer is not already an owner if (!plugin.isOwnerOf(accountA, signer)) { // should fail without owner access diff --git a/test/plugin/owner/MultiOwnerPluginIntegration.t.sol b/test/plugin/owner/MultiOwnerPluginIntegration.t.sol index f3567e7d..19991d4a 100644 --- a/test/plugin/owner/MultiOwnerPluginIntegration.t.sol +++ b/test/plugin/owner/MultiOwnerPluginIntegration.t.sol @@ -76,9 +76,9 @@ contract MultiOwnerPluginIntegration is Test { } function test_ownerPlugin_successInstallation() public { - assertTrue(IMultiOwnerPlugin(address(account)).isOwner(owner1)); - assertTrue(IMultiOwnerPlugin(address(account)).isOwner(owner2)); - assertEq(Utils.reverseAddressArray(owners), IMultiOwnerPlugin(address(account)).owners()); + assertTrue(multiOwnerPlugin.isOwnerOf(address(account), owner1)); + assertTrue(multiOwnerPlugin.isOwnerOf(address(account), owner2)); + assertEq(Utils.reverseAddressArray(owners), multiOwnerPlugin.ownersOf(address(account))); } function test_runtimeValidation_alwaysAllow_isValidSignature() public { diff --git a/test/upgrade/MSCAToMSCA.t.sol b/test/upgrade/MSCAToMSCA.t.sol index 395e1cda..67da13a4 100644 --- a/test/upgrade/MSCAToMSCA.t.sol +++ b/test/upgrade/MSCAToMSCA.t.sol @@ -66,7 +66,9 @@ contract MSCAToMSCATest is Test { msca.upgradeToAndCall(mscaImpl2, ""); // verify account storage is the same - (, bytes memory returnData) = address(msca).call(abi.encodeWithSelector(MultiOwnerPlugin.owners.selector)); + (, bytes memory returnData) = address(multiOwnerPlugin).call( + abi.encodeWithSelector(MultiOwnerPlugin.ownersOf.selector, address(msca)) + ); address[] memory returnedOwners = abi.decode(returnData, (address[])); assertEq(Utils.reverseAddressArray(returnedOwners), owners); assertEq(token1.balanceOf(address(msca)), 1 ether); From 97fe8a51eafdd36062cad7d99fef8cb8bd982624 Mon Sep 17 00:00:00 2001 From: howy <132113803+howydev@users.noreply.github.com> Date: Thu, 18 Jan 2024 11:29:21 -0500 Subject: [PATCH 054/106] fix: [spearbit-61] standardize index variable init (#71) --- src/plugins/owner/MultiOwnerPlugin.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/owner/MultiOwnerPlugin.sol b/src/plugins/owner/MultiOwnerPlugin.sol index d14f5226..0a28eaaa 100644 --- a/src/plugins/owner/MultiOwnerPlugin.sol +++ b/src/plugins/owner/MultiOwnerPlugin.sol @@ -391,7 +391,7 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { address[] memory ownersToRemove ) private { uint256 length = ownersToRemove.length; - for (uint256 i; i < length;) { + for (uint256 i = 0; i < length;) { if (!ownerSet.tryRemove(associated, CastLib.toSetValue(ownersToRemove[i]))) { revert OwnerDoesNotExist(ownersToRemove[i]); } @@ -409,7 +409,7 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { { address[] memory owners_ = ownersOf(associated); uint256 length = owners_.length; - for (uint256 i; i < length;) { + for (uint256 i = 0; i < length;) { if (SignatureChecker.isValidERC1271SignatureNow(owners_[i], digest, signature)) { return true; } From 28c7bf1c022d5a71427c58e65e5feefc3640b713 Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Thu, 18 Jan 2024 08:36:03 -0800 Subject: [PATCH 055/106] fix: [spearbit-98][quantstamp-8] State is not cached properly before validation/execution steps, Non-Idempotent Pre-Execution Hook (#58) --- src/account/UpgradeableModularAccount.sol | 431 ++--- test/account/AccountExecHooks.t.sol | 4 +- test/account/AccountLoupe.t.sol | 5 + test/account/AccountPermittedCallHooks.t.sol | 4 +- ...gradeableModularAccountPluginManager.t.sol | 2 +- test/account/phases/AccountStatePhases.t.sol | 310 ++++ .../phases/AccountStatePhasesExec.t.sol | 1309 ++++++++++++++++ .../AccountStatePhasesRTValidation.t.sol | 1387 +++++++++++++++++ .../AccountStatePhasesUOValidation.t.sol | 1341 ++++++++++++++++ .../plugins/AccountStateMutatingPlugin.sol | 280 ++++ test/mocks/plugins/ComprehensivePlugin.sol | 3 + test/plugin/TokenReceiverPlugin.t.sol | 6 +- 12 files changed, 4893 insertions(+), 189 deletions(-) create mode 100644 test/account/phases/AccountStatePhases.t.sol create mode 100644 test/account/phases/AccountStatePhasesExec.t.sol create mode 100644 test/account/phases/AccountStatePhasesRTValidation.t.sol create mode 100644 test/account/phases/AccountStatePhasesUOValidation.t.sol create mode 100644 test/mocks/plugins/AccountStateMutatingPlugin.sol diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index 2cc5842d..ec7fc1d4 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -129,28 +129,25 @@ contract UpgradeableModularAccount is /// this function selector, revert. /// @return Data returned from the called execution function. fallback(bytes calldata) external payable returns (bytes memory) { - SelectorData storage selectorData = _getAccountStorage().selectorData[msg.sig]; - - address execPlugin = selectorData.plugin; - if (execPlugin == address(0)) { - revert UnrecognizedFunction(msg.sig); - } - // Either reuse the call buffer from runtime validation, or allocate a new one. It may or may not be used // for pre exec hooks but it will be used for the plugin execution itself. bytes memory callBuffer = (msg.sender != address(_ENTRY_POINT)) ? _doRuntimeValidation() : _allocateRuntimeCallBuffer(msg.data); - bool hasPreExecHooks = selectorData.hasPreExecHooks; - bool hasPostOnlyExecHooks = selectorData.hasPostOnlyExecHooks; - - FunctionReference[] memory postExecHooksToRun; - bytes[] memory postExecHookArgs; - if (hasPreExecHooks) { - // Cache post-exec hooks in memory - (postExecHooksToRun, postExecHookArgs) = _doPreExecHooks(msg.sig, callBuffer); + // To comply with ERC-6900 phase rules, defer the loading of execution phase data until the completion of + // runtime validation. + // Validation may update account state and therefore change execution phase data. These values should also + // be loaded before + // we run the pre exec hooks, because they may modify which plugin is defined. + SelectorData storage selectorData = _getAccountStorage().selectorData[msg.sig]; + address execPlugin = selectorData.plugin; + if (execPlugin == address(0)) { + revert UnrecognizedFunction(msg.sig); } + (FunctionReference[][] memory postHooksToRun, bytes[] memory postHookArgs) = + _doPreExecHooks(selectorData, callBuffer); + // execute the function, bubbling up any reverts bool execSuccess = _executeRaw(execPlugin, _convertRuntimeCallBufferToExecBuffer(callBuffer)); bytes memory execReturnData = _collectReturnData(); @@ -162,14 +159,7 @@ contract UpgradeableModularAccount is } } - _doCachedPostHooks(postExecHooksToRun, postExecHookArgs); - - if (hasPostOnlyExecHooks) { - _doCachedPostHooks( - CastLib.toFunctionReferenceArray(selectorData.executionHooks.postOnlyHooks.getAll()), - new bytes[](0) - ); - } + _doCachedPostHooks(postHooksToRun, postHookArgs); return execReturnData; } @@ -185,13 +175,11 @@ contract UpgradeableModularAccount is revert UserOpNotFromEntryPoint(); } - bool hasPreValidationHooks; - bytes4 selector = _selectorFromCallData(userOp.callData); SelectorData storage selectorData = _getAccountStorage().selectorData[selector]; FunctionReference userOpValidationFunction = selectorData.userOpValidation; - hasPreValidationHooks = selectorData.hasPreUserOpValidationHooks; + bool hasPreValidationHooks = selectorData.hasPreUserOpValidationHooks; validationData = _doUserOpValidation(selector, userOpValidationFunction, userOp, userOpHash, hasPreValidationHooks); @@ -210,14 +198,14 @@ contract UpgradeableModularAccount is override returns (bytes memory result) { - (FunctionReference[] memory postExecHooks, bytes[] memory postExecHookArgs) = _preNativeFunction(); + (FunctionReference[][] memory postExecHooks, bytes[] memory postHookArgs) = _preNativeFunction(); result = _exec(target, value, data); - _postNativeFunction(postExecHooks, postExecHookArgs); + _postNativeFunction(postExecHooks, postHookArgs); } /// @inheritdoc IStandardExecutor function executeBatch(Call[] calldata calls) external payable override returns (bytes[] memory results) { - (FunctionReference[] memory postExecHooks, bytes[] memory postExecHookArgs) = _preNativeFunction(); + (FunctionReference[][] memory postExecHooks, bytes[] memory postHookArgs) = _preNativeFunction(); uint256 callsLength = calls.length; results = new bytes[](callsLength); @@ -230,7 +218,7 @@ contract UpgradeableModularAccount is } } - _postNativeFunction(postExecHooks, postExecHookArgs); + _postNativeFunction(postExecHooks, postHookArgs); } /// @inheritdoc IPluginExecutor @@ -247,28 +235,17 @@ contract UpgradeableModularAccount is bytes memory callBuffer = _allocateRuntimeCallBuffer(data); - FunctionReference[] memory postPermittedCallHooks; - bytes[] memory postPermittedCallHookArgs; - if (permittedCallData.hasPrePermittedCallHooks) { - // Cache post-permitted call hooks in memory - (postPermittedCallHooks, postPermittedCallHookArgs) = - _doPrePermittedCallHooks(permittedCallKey, callBuffer); - } - SelectorData storage selectorData = storage_.selectorData[selector]; + // Load the plugin address from storage prior to running any hooks, to abide by the ERC-6900 phase rules. address execFunctionPlugin = selectorData.plugin; + (FunctionReference[][] memory postHooksToRun, bytes[] memory postHookArgs) = + _doPrePermittedCallHooksAndPreExecHooks(selectorData, permittedCallData, callBuffer); + if (execFunctionPlugin == address(0)) { revert UnrecognizedFunction(selector); } - FunctionReference[] memory postExecHooks; - bytes[] memory postExecHookArgs; - if (selectorData.hasPreExecHooks) { - // Cache post-exec hooks in memory - (postExecHooks, postExecHookArgs) = _doPreExecHooks(selector, callBuffer); - } - bool success = _executeRaw(execFunctionPlugin, _convertRuntimeCallBufferToExecBuffer(callBuffer)); returnData = _collectReturnData(); @@ -278,23 +255,7 @@ contract UpgradeableModularAccount is } } - _doCachedPostHooks(postExecHooks, postExecHookArgs); - - if (selectorData.hasPostOnlyExecHooks) { - _doCachedPostHooks( - CastLib.toFunctionReferenceArray(selectorData.executionHooks.postOnlyHooks.getAll()), - new bytes[](0) - ); - } - - _doCachedPostHooks(postPermittedCallHooks, postPermittedCallHookArgs); - - if (permittedCallData.hasPostOnlyPermittedCallHooks) { - _doCachedPostHooks( - CastLib.toFunctionReferenceArray(permittedCallData.permittedCallHooks.postOnlyHooks.getAll()), - new bytes[](0) - ); - } + _doCachedPostHooks(postHooksToRun, postHookArgs); return returnData; } @@ -347,49 +308,21 @@ contract UpgradeableModularAccount is revert ExecFromPluginExternalNotPermitted(callingPlugin, target, value, data); } - // Run permitted call hooks and execution hooks. `executeFromPluginExternal` doesn't use PermittedCallData - // to check call permissions, nor do they have an address entry in SelectorData, so it doesn't make sense - // to use cached booleans (hasPreExecHooks, hasPostOnlyExecHooks, etc.) to conditionally bypass certain - // steps, as it would just be an added `sload` in the nonzero hooks case. + // Run any pre permitted call hooks specific to this caller and the `executeFromPluginExternal` selector, + // then run any pre-exec hooks associated with the `executeFromPluginExternal` selector. + PermittedCallData storage permittedCallData = storage_.permittedCalls[_getPermittedCallKey( + callingPlugin, IPluginExecutor.executeFromPluginExternal.selector + )]; + SelectorData storage selectorData = + storage_.selectorData[IPluginExecutor.executeFromPluginExternal.selector]; - // Run any pre permitted call hooks specific to this caller and the `executeFromPluginExternal` selector - bytes24 permittedCallKey = - _getPermittedCallKey(callingPlugin, IPluginExecutor.executeFromPluginExternal.selector); - (FunctionReference[] memory postPermittedCallHooks, bytes[] memory postPermittedCallHookArgs) = - _doPrePermittedCallHooks(permittedCallKey, ""); - - // Run any pre exec hooks for the `executeFromPluginExternal` selector - (FunctionReference[] memory postExecHooks, bytes[] memory postExecHookArgs) = - _doPreExecHooks(IPluginExecutor.executeFromPluginExternal.selector, ""); + (FunctionReference[][] memory postHooksToRun, bytes[] memory postHookArgs) = + _doPrePermittedCallHooksAndPreExecHooks(selectorData, permittedCallData, ""); // Perform the external call bytes memory returnData = _exec(target, value, data); - // Run any post exec hooks for the `executeFromPluginExternal` selector - _doCachedPostHooks(postExecHooks, postExecHookArgs); - - // Run any post only exec hooks for the `executeFromPluginExternal` selector - _doCachedPostHooks( - CastLib.toFunctionReferenceArray( - storage_.selectorData[IPluginExecutor.executeFromPluginExternal.selector] - .executionHooks - .postOnlyHooks - .getAll() - ), - new bytes[](0) - ); - - // Run any post permitted call hooks specific to this caller and the `executeFromPluginExternal` selector - _doCachedPostHooks(postPermittedCallHooks, postPermittedCallHookArgs); - - // Run any post only permitted call hooks specific to this caller and the `executeFromPluginExternal` - // selector - _doCachedPostHooks( - CastLib.toFunctionReferenceArray( - storage_.permittedCalls[permittedCallKey].permittedCallHooks.postOnlyHooks.getAll() - ), - new bytes[](0) - ); + _doCachedPostHooks(postHooksToRun, postHookArgs); return returnData; } @@ -402,7 +335,7 @@ contract UpgradeableModularAccount is FunctionReference[] calldata dependencies, InjectedHook[] calldata injectedHooks ) external override { - (FunctionReference[] memory postExecHooks, bytes[] memory postHookArgs) = _preNativeFunction(); + (FunctionReference[][] memory postExecHooks, bytes[] memory postHookArgs) = _preNativeFunction(); _installPlugin(plugin, manifestHash, pluginInitData, dependencies, injectedHooks); _postNativeFunction(postExecHooks, postHookArgs); } @@ -414,7 +347,7 @@ contract UpgradeableModularAccount is bytes calldata pluginUninstallData, bytes[] calldata hookUnapplyData ) external override { - (FunctionReference[] memory postExecHooks, bytes[] memory postHookArgs) = _preNativeFunction(); + (FunctionReference[][] memory postExecHooks, bytes[] memory postHookArgs) = _preNativeFunction(); UninstallPluginArgs memory args; args.plugin = plugin; @@ -455,7 +388,7 @@ contract UpgradeableModularAccount is /// @inheritdoc UUPSUpgradeable function upgradeToAndCall(address newImplementation, bytes calldata data) public payable override onlyProxy { - (FunctionReference[] memory postExecHooks, bytes[] memory postHookArgs) = _preNativeFunction(); + (FunctionReference[][] memory postExecHooks, bytes[] memory postHookArgs) = _preNativeFunction(); UUPSUpgradeable.upgradeToAndCall(newImplementation, data); _postNativeFunction(postExecHooks, postHookArgs); } @@ -476,7 +409,7 @@ contract UpgradeableModularAccount is /// execute, executeBatch, installPlugin, uninstallPlugin. function _preNativeFunction() internal - returns (FunctionReference[] memory postExecHooks, bytes[] memory postExecHookArgs) + returns (FunctionReference[][] memory postExecHooks, bytes[] memory postHookArgs) { bytes memory callBuffer = ""; @@ -484,22 +417,15 @@ contract UpgradeableModularAccount is callBuffer = _doRuntimeValidation(); } - (postExecHooks, postExecHookArgs) = _doPreExecHooks(msg.sig, callBuffer); + (postExecHooks, postHookArgs) = _doPreExecHooks(_getAccountStorage().selectorData[msg.sig], callBuffer); } /// @dev Wraps execution of a native function with runtime validation and hooks. Used for upgradeToAndCall, /// execute, executeBatch, installPlugin, uninstallPlugin. - function _postNativeFunction(FunctionReference[] memory postExecHooks, bytes[] memory postExecHookArgs) + function _postNativeFunction(FunctionReference[][] memory postExecHooks, bytes[] memory postHookArgs) internal { - _doCachedPostHooks(postExecHooks, postExecHookArgs); - - _doCachedPostHooks( - CastLib.toFunctionReferenceArray( - _getAccountStorage().selectorData[msg.sig].executionHooks.postOnlyHooks.getAll() - ), - new bytes[](0) - ); + _doCachedPostHooks(postExecHooks, postHookArgs); } /// @dev To support gas estimation, we don't fail early when the failure is caused by a signature failure. @@ -647,54 +573,162 @@ contract UpgradeableModularAccount is } } - function _doPreExecHooks(bytes4 selector, bytes memory callBuffer) + /// @dev Executes pre-exec hooks and returns the post-exec hooks to run and their associated args. + function _doPreExecHooks(SelectorData storage selectorData, bytes memory callBuffer) internal - returns (FunctionReference[] memory, bytes[] memory) + returns (FunctionReference[][] memory postHooksToRun, bytes[] memory postHookArgs) { - SelectorData storage selectorData = _getAccountStorage().selectorData[selector]; - return _doPreHooks( - selectorData.executionHooks.preHooks, selectorData.executionHooks.associatedPostHooks, callBuffer - ); - } + FunctionReference[] memory preExecHooks; - function _doPrePermittedCallHooks(bytes24 permittedCallKey, bytes memory callBuffer) - internal - returns (FunctionReference[] memory, bytes[] memory) - { - PermittedCallData storage permittedCallData = _getAccountStorage().permittedCalls[permittedCallKey]; - return _doPreHooks( - permittedCallData.permittedCallHooks.preHooks, - permittedCallData.permittedCallHooks.associatedPostHooks, - callBuffer - ); + bool hasPreExecHooks = selectorData.hasPreExecHooks; + bool hasPostOnlyExecHooks = selectorData.hasPostOnlyExecHooks; + + if (hasPreExecHooks) { + preExecHooks = CastLib.toFunctionReferenceArray(selectorData.executionHooks.preHooks.getAll()); + } + + // Allocate memory for the post hooks and post hook args. + // If we have post-only hooks, we allocate an extra FunctionReference[] for them, and one extra element + // in the args for their empty `bytes` argument. + uint256 postHooksToRunLength = preExecHooks.length + (hasPostOnlyExecHooks ? 1 : 0); + postHooksToRun = new FunctionReference[][](postHooksToRunLength); + postHookArgs = new bytes[](postHooksToRunLength); + + uint256 currentIndex = 0; + + if (hasPostOnlyExecHooks) { + // If we have post-only hooks, we allocate an single FunctionReference[] for them, and one element + // in the args for their empty `bytes` argument. We put this into the first element of the post + // hooks in order to have it run last. + postHooksToRun[0] = + CastLib.toFunctionReferenceArray(selectorData.executionHooks.postOnlyHooks.getAll()); + unchecked { + ++currentIndex; + } + } + + // If there are no pre exec hooks, this will short-circuit in the length check on `preExecHooks`. + _cacheAssociatedPostHooks(preExecHooks, selectorData.executionHooks, postHooksToRun, currentIndex); + + // Run all pre-exec hooks and capture their outputs. + _doPreHooks(preExecHooks, callBuffer, postHooksToRun, postHookArgs, currentIndex); } - function _doPreHooks( - LinkedListSet storage preHookSet, - mapping(FunctionReference => LinkedListSet) storage associatedPostHooks, + /// @dev Executes pre-permitted call hooks and pre-exec hooks, and returns the post-exec hooks to run and + /// their associated args. + function _doPrePermittedCallHooksAndPreExecHooks( + SelectorData storage selectorData, + PermittedCallData storage permittedCallData, bytes memory callBuffer - ) internal returns (FunctionReference[] memory postHooks, bytes[] memory postHookArgs) { - FunctionReference[] memory preExecHooks = CastLib.toFunctionReferenceArray(preHookSet.getAll()); + ) internal returns (FunctionReference[][] memory postHooksToRun, bytes[] memory postHookArgs) { + FunctionReference[] memory prePermittedCallHooks; + FunctionReference[] memory preExecHooks; - uint256 preExecHooksLength = preExecHooks.length; - uint256 maxPostHooksToRunLength; + bool hasPrePermittedCallHooks = permittedCallData.hasPrePermittedCallHooks; + bool hasPostOnlyPermittedCallHooks = permittedCallData.hasPostOnlyPermittedCallHooks; - // There can only be as many associated post hooks to run as there are pre hooks. - for (uint256 i = 0; i < preExecHooksLength;) { + bool hasPreExecHooks = selectorData.hasPreExecHooks; + bool hasPostOnlyExecHooks = selectorData.hasPostOnlyExecHooks; + + // If we have any type of pre hooks, we need to allocate memory for them to perform their call. + if (callBuffer.length == 0 && (hasPrePermittedCallHooks || hasPreExecHooks)) { + callBuffer = _allocateRuntimeCallBuffer(msg.data); + } + + if (hasPrePermittedCallHooks) { + prePermittedCallHooks = + CastLib.toFunctionReferenceArray(permittedCallData.permittedCallHooks.preHooks.getAll()); + } + + if (hasPreExecHooks) { + preExecHooks = CastLib.toFunctionReferenceArray(selectorData.executionHooks.preHooks.getAll()); + } + + // Allocate memory for the post hooks and post hook args. + // If we have post-only hooks, we allocate an extra FunctionReference[] for them, and one extra element in + // the args for their empty `bytes` argument. + uint256 postHooksToRunLength = prePermittedCallHooks.length + preExecHooks.length + + (hasPostOnlyPermittedCallHooks ? 1 : 0) + (hasPostOnlyExecHooks ? 1 : 0); + postHooksToRun = new FunctionReference[][](postHooksToRunLength); + postHookArgs = new bytes[](postHooksToRunLength); + + // The order we want to fill the post exec hooks is: + // 1. Post-only permitted call hooks + // 2. Associated post hooks for pre-permitted call hooks + // 3. Post-only exec hooks + // 4. Associated post hooks for pre-exec hooks + + uint256 associatedPermittedCallHooksIndex = 0; + + if (hasPostOnlyPermittedCallHooks) { + // If we have post-only hooks, we allocate an single FunctionReference[] for them, and one element in + // the args for their empty `bytes` argument. We put this into the first element of the post hooks in + // order to have it run last. + postHooksToRun[associatedPermittedCallHooksIndex] = + CastLib.toFunctionReferenceArray(permittedCallData.permittedCallHooks.postOnlyHooks.getAll()); unchecked { - maxPostHooksToRunLength += preHookSet.getCount(CastLib.toSetValue(preExecHooks[i])); - ++i; + ++associatedPermittedCallHooksIndex; + } + } + + // Cache post-permitted-call hooks in memory + _cacheAssociatedPostHooks( + prePermittedCallHooks, + permittedCallData.permittedCallHooks, + postHooksToRun, + associatedPermittedCallHooksIndex + ); + // The exec hooks start after the permitted call hooks + uint256 associatedExecHooksIndex; + unchecked { + associatedExecHooksIndex = associatedPermittedCallHooksIndex + prePermittedCallHooks.length; + } + + if (hasPostOnlyExecHooks) { + // If we have post-only hooks, we allocate an single FunctionReference[] for them, and one element in + // the args for their empty `bytes` argument. We put this into the first element of the post hooks in + // order to have it run last. + postHooksToRun[associatedExecHooksIndex] = + CastLib.toFunctionReferenceArray(selectorData.executionHooks.postOnlyHooks.getAll()); + unchecked { + ++associatedExecHooksIndex; } } - // Overallocate on length, but not all of this may get filled up. - postHooks = new FunctionReference[](maxPostHooksToRunLength); - postHookArgs = new bytes[](maxPostHooksToRunLength); - uint256 actualPostHooksToRunLength; + // Cache post-exec hooks in memory + _cacheAssociatedPostHooks( + preExecHooks, selectorData.executionHooks, postHooksToRun, associatedExecHooksIndex + ); + + // Run the permitted call hooks + _doPreHooks( + prePermittedCallHooks, callBuffer, postHooksToRun, postHookArgs, associatedPermittedCallHooksIndex + ); + + // Run the pre-exec hooks + _doPreHooks(preExecHooks, callBuffer, postHooksToRun, postHookArgs, associatedExecHooksIndex); + } + + /// @dev Execute all pre hooks provided, using the call buffer if provided. + /// Outputs are captured into the `hookReturnData` array, in increasing index starting at `startingIndex`. + /// The `postHooks` array is used to determine whether or not to capture the return data. + /// NOTE: The caller must ensure that: + /// - `postHooks` is allocated, and `startingIndex + preHooks.length` does not exceed the array bounds of + /// `postHooks`. + /// - `hookReturnData` is allocated, and `startingIndex + preHooks.length` does not exceed the array bounds of + /// `hookReturnData`. + function _doPreHooks( + FunctionReference[] memory preHooks, + bytes memory callBuffer, + FunctionReference[][] memory postHooks, // Only used to check if any post hooks exist. + bytes[] memory hookReturnData, + uint256 startingIndex // Where to start writing into hookReturnData + ) internal { + uint256 preExecHooksLength = preHooks.length; // If not running anything, short-circuit before allocating more memory for the call buffers. if (preExecHooksLength == 0) { - return (postHooks, postHookArgs); + return; } if (callBuffer.length == 0) { @@ -706,7 +740,7 @@ contract UpgradeableModularAccount is _updatePluginCallBufferSelector(callBuffer, IPlugin.preExecutionHook.selector); for (uint256 i = 0; i < preExecHooksLength;) { - FunctionReference preExecHook = preExecHooks[i]; + FunctionReference preExecHook = preHooks[i]; if (preExecHook.isEmptyOrMagicValue()) { // The function reference must be the Always Deny magic value in this case, @@ -718,50 +752,54 @@ contract UpgradeableModularAccount is _updatePluginCallBufferFunctionId(callBuffer, functionId); - if (preHookSet.flagsEnabled(CastLib.toSetValue(preExecHook), _PRE_EXEC_HOOK_HAS_POST_FLAG)) { - FunctionReference[] memory associatedPostExecHooks = - CastLib.toFunctionReferenceArray(associatedPostHooks[preExecHook].getAll()); - uint256 associatedPostExecHooksLength = associatedPostExecHooks.length; + _executeRuntimePluginFunction(callBuffer, plugin, PreExecHookReverted.selector); - for (uint256 j = 0; j < associatedPostExecHooksLength;) { - // Execute the pre-hook as many times as there are unique associated post-hooks. - _executeRuntimePluginFunction(callBuffer, plugin, PreExecHookReverted.selector); - - postHooks[actualPostHooksToRunLength] = associatedPostExecHooks[j]; - postHookArgs[actualPostHooksToRunLength] = abi.decode(_collectReturnData(), (bytes)); + uint256 adjustedIndex; + unchecked { + adjustedIndex = startingIndex + i; + } - unchecked { - ++actualPostHooksToRunLength; - ++j; - } - } - } else { - _executeRuntimePluginFunction(callBuffer, plugin, PreExecHookReverted.selector); + // Only collect the return data if there is at least one post-hook to consume it. + if (postHooks[adjustedIndex].length > 0) { + hookReturnData[adjustedIndex] = abi.decode(_collectReturnData(), (bytes)); } unchecked { ++i; } } - - // "Trim" the associated post hook arrays to the actual length, since we may have overallocated. This - // allows for execution of post hooks in reverse order. - assembly ("memory-safe") { - mstore(postHooks, actualPostHooksToRunLength) - mstore(postHookArgs, actualPostHooksToRunLength) - } } - function _doCachedPostHooks(FunctionReference[] memory postHooks, bytes[] memory postHookArgs) internal { - uint256 postHooksToRunLength = postHooks.length; - bool hasPostHookArgs = postHookArgs.length > 0; - for (uint256 i = postHooksToRunLength; i > 0;) { - FunctionReference postExecHook = postHooks[i - 1]; - (address plugin, uint8 functionId) = postExecHook.unpack(); - // solhint-disable-next-line no-empty-blocks - try IPlugin(plugin).postExecutionHook(functionId, hasPostHookArgs ? postHookArgs[i - 1] : bytes("")) {} - catch (bytes memory revertReason) { - revert PostExecHookReverted(plugin, functionId, revertReason); + /// @dev Executes all post hooks in the nested array, using the corresponding args in the nested array. + /// Executes the elements in reverse order, so the caller should ensure the correct ordering before calling. + function _doCachedPostHooks(FunctionReference[][] memory postHooks, bytes[] memory postHookArgs) internal { + // Run post hooks in reverse order of their associated pre hooks. + uint256 postHookArrsLength = postHooks.length; + for (uint256 i = postHookArrsLength; i > 0;) { + uint256 index; + unchecked { + // i starts as the length of the array and goes to 1, not zero, to avoid underflowing. + // To use the index for array access, we need to subtract 1. + index = i - 1; + } + FunctionReference[] memory postHooksToRun = postHooks[index]; + + // We don't need to run each associated post-hook in reverse order, because the associativity we want + // to maintain is reverse order of associated pre-hooks. + uint256 postHooksToRunLength = postHooksToRun.length; + for (uint256 j = 0; j < postHooksToRunLength;) { + (address plugin, uint8 functionId) = postHooksToRun[j].unpack(); + + // Execute the post hook with the current post hook args + // solhint-disable-next-line no-empty-blocks + try IPlugin(plugin).postExecutionHook(functionId, postHookArgs[index]) {} + catch (bytes memory revertReason) { + revert PostExecHookReverted(plugin, functionId, revertReason); + } + + unchecked { + ++j; + } } unchecked { @@ -774,6 +812,35 @@ contract UpgradeableModularAccount is // solhint-disable-next-line no-empty-blocks function _authorizeUpgrade(address newImplementation) internal override {} + /// @dev Loads the associated post hooks for the given pre-exec hooks in the `postHooks` array, starting at + /// `startingIndex`. + /// NOTE: The caller must ensure that `postHooks` is allocated, and `startingIndex + preHooks.length` does not + // exceed the array bounds of `postHooks`. + function _cacheAssociatedPostHooks( + FunctionReference[] memory preExecHooks, + HookGroup storage hookGroup, + FunctionReference[][] memory postHooks, + uint256 startingIndex + ) internal view { + uint256 preExecHooksLength = preExecHooks.length; + for (uint256 i = 0; i < preExecHooksLength;) { + FunctionReference preExecHook = preExecHooks[i]; + + // If the pre-exec hook has associated post hooks, cache them in the postHooks array. + if (hookGroup.preHooks.flagsEnabled(CastLib.toSetValue(preExecHook), _PRE_EXEC_HOOK_HAS_POST_FLAG)) { + // We start writing into the postHooks array starting at the `startingIndex` and counting up. + postHooks[startingIndex + i] = + CastLib.toFunctionReferenceArray(hookGroup.associatedPostHooks[preExecHook].getAll()); + } + // In no-associated-post-hooks case, we're OK returning the default value, which is an array of length + // 0. + + unchecked { + ++i; + } + } + } + /// @dev Revert with an appropriate error if the calldata does not include a function selector. function _selectorFromCallData(bytes calldata data) internal pure returns (bytes4) { if (data.length < 4) { diff --git a/test/account/AccountExecHooks.t.sol b/test/account/AccountExecHooks.t.sol index 1130fc26..0df93c74 100644 --- a/test/account/AccountExecHooks.t.sol +++ b/test/account/AccountExecHooks.t.sol @@ -728,7 +728,7 @@ contract UpgradeableModularAccountExecHooksTest is Test { /// @dev Plugin 1 hook pair: [1, 2] /// Plugin 2 hook pair: [1, 4] - /// Expected execution: [1, 2], [1, 4] + /// Expected execution: [1, 2], [null, 4] function test_overlappingExecHookPairsOnPre_run() public { test_overlappingExecHookPairsOnPre_install(); @@ -744,7 +744,7 @@ contract UpgradeableModularAccountExecHooksTest is Test { 0, // msg.value in call to account abi.encodeWithSelector(_EXEC_SELECTOR) ), - 2 + 1 ); // Expect each post hook to be called once, with the expected data. diff --git a/test/account/AccountLoupe.t.sol b/test/account/AccountLoupe.t.sol index efbf8702..0127034b 100644 --- a/test/account/AccountLoupe.t.sol +++ b/test/account/AccountLoupe.t.sol @@ -567,4 +567,9 @@ contract AccountLoupeTest is Test { assertEq(FunctionReference.unwrap(hook.preExecHook), FunctionReference.unwrap(preHook)); assertEq(FunctionReference.unwrap(hook.postExecHook), FunctionReference.unwrap(postHook)); } + + function test_trace_comprehensivePlugin() public { + vm.prank(address(comprehensivePlugin)); + account1.executeFromPlugin(abi.encodeCall(comprehensivePlugin.foo, ())); + } } diff --git a/test/account/AccountPermittedCallHooks.t.sol b/test/account/AccountPermittedCallHooks.t.sol index 11fc86bf..7bef307e 100644 --- a/test/account/AccountPermittedCallHooks.t.sol +++ b/test/account/AccountPermittedCallHooks.t.sol @@ -537,7 +537,7 @@ contract UpgradeableModularAccountPermittedCallHooksTest is Test { } /// @dev Plugin hook pair(s): [1, 2], [1, 4] - /// Expected execution: [1, 2], [1, 4] + /// Expected execution: [1, 2], [null, 4] function test_overlappingPermittedCallHookPairsOnPre_run() public { test_overlappingPermittedCallHookPairsOnPre_install(); @@ -553,7 +553,7 @@ contract UpgradeableModularAccountPermittedCallHooksTest is Test { 0, // msg.value in call to account abi.encodePacked(_EXEC_SELECTOR) ), - 2 + 1 ); // Expect each post hook to be called once, with the expected data. diff --git a/test/account/UpgradeableModularAccountPluginManager.t.sol b/test/account/UpgradeableModularAccountPluginManager.t.sol index ac1ce1b7..c70c1712 100644 --- a/test/account/UpgradeableModularAccountPluginManager.t.sol +++ b/test/account/UpgradeableModularAccountPluginManager.t.sol @@ -537,7 +537,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { // manifest for uninstallation despite being given the old one). vm.expectRevert( abi.encodeWithSelector( - UpgradeableModularAccount.UnrecognizedFunction.selector, + UpgradeableModularAccount.RuntimeValidationFunctionMissing.selector, CanChangeManifestPlugin.someExecutionFunction.selector ) ); diff --git a/test/account/phases/AccountStatePhases.t.sol b/test/account/phases/AccountStatePhases.t.sol new file mode 100644 index 00000000..5b0a1880 --- /dev/null +++ b/test/account/phases/AccountStatePhases.t.sol @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.21; + +import {Test} from "forge-std/Test.sol"; + +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; + +import {UpgradeableModularAccount} from "../../../src/account/UpgradeableModularAccount.sol"; +import {MultiOwnerPlugin} from "../../../src/plugins/owner/MultiOwnerPlugin.sol"; +import {IEntryPoint} from "../../../src/interfaces/erc4337/IEntryPoint.sol"; +import {UserOperation} from "../../../src/interfaces/erc4337/UserOperation.sol"; +import {IPluginManager} from "../../../src/interfaces/IPluginManager.sol"; +import {IStandardExecutor, Call} from "../../../src/interfaces/IStandardExecutor.sol"; +import { + IPlugin, + ManifestExecutionHook, + PluginManifest, + ManifestFunction, + ManifestAssociatedFunctionType, + ManifestAssociatedFunction +} from "../../../src/interfaces/IPlugin.sol"; +import {FunctionReference, FunctionReferenceLib} from "../../../src/libraries/FunctionReferenceLib.sol"; +import {MultiOwnerMSCAFactory} from "../../../src/factory/MultiOwnerMSCAFactory.sol"; + +import {AccountStateMutatingPlugin} from "../../mocks/plugins/AccountStateMutatingPlugin.sol"; +import {MockPlugin} from "../../mocks/MockPlugin.sol"; + +// A test suite that verifies how the account caches the state of plugins. This is intended to ensure consistency +// of execution flow when either hooks or plugins change installation state within a single call to the account. +// The following tests inherit from this test base: +// - AccountStatePhasesUOValidationTest +// - AccountStatePhasesRTValidationTest +// - AccountStatePhasesExecTest +// NOTE: This test implicitly depends on hooks execution order being latest-to-oldest. This is not guaranteed by +// the spec, but is currently the case. If that changes, this test will need to be updated. +// How these tests will work +// - Create a custom plugin "AccountStateMutatingPlugin" that can perform install / uninstall during hooks, +// validation, or execution. +// - This is done by pushing the call encoding responsibilitiy to this test, and just exposing a "side" +// method that specifies the callback it should do in a given phase back toward the calling account. +// - Authorization for install/uninstall can be granted by making the plugin itself an owner in multi-owner +// plugin, which will authorize runtime calls. +// - The contents of what is called are defined in a mock plugin like the exec hooks test. +contract AccountStatePhasesTest is Test { + using ECDSA for bytes32; + + IEntryPoint public entryPoint; + MultiOwnerPlugin public multiOwnerPlugin; + MultiOwnerMSCAFactory public factory; + address payable beneficiary; + + address public owner1; + uint256 public owner1Key; + UpgradeableModularAccount public account1; + + AccountStateMutatingPlugin public asmPlugin; + + MockPlugin public mockPlugin1; + bytes32 public manifestHash1; + PluginManifest public m1; + + // Function ID constants to use with the mock plugin. + uint8 internal constant _PRE_HOOK_FUNCTION_ID_1 = 1; + uint8 internal constant _POST_HOOK_FUNCTION_ID_2 = 2; + uint8 internal constant _PRE_UO_VALIDATION_HOOK_FUNCTION_ID_3 = 3; + uint8 internal constant _PRE_RT_VALIDATION_HOOK_FUNCTION_ID_4 = 4; + uint8 internal constant _UO_VALIDATION_FUNCTION_ID_5 = 5; + uint8 internal constant _RT_VALIDATION_FUNCTION_ID_6 = 6; + + // Event re-declarations for vm.expectEmit + event PluginInstalled( + address indexed plugin, + bytes32 manifestHash, + FunctionReference[] dependencies, + IPluginManager.InjectedHook[] injectedHooks + ); + event PluginUninstalled(address indexed plugin, bool indexed callbacksSucceeded); + event ReceivedCall(bytes msgData, uint256 msgValue); + + // Empty arrays for convenience + FunctionReference[] internal _EMPTY_DEPENDENCIES; + IPluginManager.InjectedHook[] internal _EMPTY_INJECTED_HOOKS; + bytes[] internal _EMPTY_HOOK_APPLY_DATA; + + // Constants for running user ops + uint256 constant CALL_GAS_LIMIT = 300000; + uint256 constant VERIFICATION_GAS_LIMIT = 1000000; + + function setUp() public { + entryPoint = IEntryPoint(address(new EntryPoint())); + multiOwnerPlugin = new MultiOwnerPlugin(); + asmPlugin = new AccountStateMutatingPlugin(); + + (owner1, owner1Key) = makeAddrAndKey("owner1"); + beneficiary = payable(makeAddr("beneficiary")); + address accountImpl = address(new UpgradeableModularAccount(IEntryPoint(address(entryPoint)))); + + factory = new MultiOwnerMSCAFactory( + address(this), + address(multiOwnerPlugin), + accountImpl, + keccak256(abi.encode(multiOwnerPlugin.pluginManifest())), + entryPoint + ); + + // Add 2 owners to the account: + // - The owner1 EOA, for signing user operations + // - The AccountStateMutatingPlugin, for authorizing runtime calls to installPlugin/uninstallPlugin + address[] memory owners = new address[](2); + owners[0] = owner1; + owners[1] = address(asmPlugin); + account1 = UpgradeableModularAccount(payable(factory.createAccount(0, owners))); + vm.deal(address(account1), 100 ether); + } + + // HELPER FUNCTIONS + + // Mock plugin config helpers - shortcuts to configure with 1 plugin function. + // Does not install the mock plugin. + + function _initMockPluginPreUserOpValidationHook() internal { + m1.preUserOpValidationHooks.push( + ManifestAssociatedFunction({ + executionSelector: AccountStateMutatingPlugin.executionFunction.selector, + associatedFunction: ManifestFunction({ + functionType: ManifestAssociatedFunctionType.SELF, + functionId: _PRE_UO_VALIDATION_HOOK_FUNCTION_ID_3, + dependencyIndex: 0 // unused + }) + }) + ); + _initMockPlugin(); + } + + function _initMockPluginUserOpValidationFunction() internal { + m1.userOpValidationFunctions.push( + ManifestAssociatedFunction({ + executionSelector: AccountStateMutatingPlugin.executionFunction.selector, + associatedFunction: ManifestFunction({ + functionType: ManifestAssociatedFunctionType.SELF, + functionId: _UO_VALIDATION_FUNCTION_ID_5, + dependencyIndex: 0 // unused + }) + }) + ); + _initMockPlugin(); + } + + function _initMockPluginPreRuntimeValidationHook() internal { + m1.preRuntimeValidationHooks.push( + ManifestAssociatedFunction({ + executionSelector: AccountStateMutatingPlugin.executionFunction.selector, + associatedFunction: ManifestFunction({ + functionType: ManifestAssociatedFunctionType.SELF, + functionId: _PRE_RT_VALIDATION_HOOK_FUNCTION_ID_4, + dependencyIndex: 0 // unused + }) + }) + ); + _initMockPlugin(); + } + + function _initMockPluginRuntimeValidationFunction() internal { + m1.runtimeValidationFunctions.push( + ManifestAssociatedFunction({ + executionSelector: AccountStateMutatingPlugin.executionFunction.selector, + associatedFunction: ManifestFunction({ + functionType: ManifestAssociatedFunctionType.SELF, + functionId: _RT_VALIDATION_FUNCTION_ID_6, + dependencyIndex: 0 // unused + }) + }) + ); + _initMockPlugin(); + } + + function _initMockPluginPreExecutionHook() internal { + m1.executionHooks.push( + ManifestExecutionHook({ + executionSelector: AccountStateMutatingPlugin.executionFunction.selector, + preExecHook: ManifestFunction({ + functionType: ManifestAssociatedFunctionType.SELF, + functionId: _PRE_HOOK_FUNCTION_ID_1, + dependencyIndex: 0 // Unused + }), + postExecHook: ManifestFunction({ + functionType: ManifestAssociatedFunctionType.NONE, + functionId: 0, // Unused + dependencyIndex: 0 // Unused + }) + }) + ); + _initMockPlugin(); + } + + function _initMockPluginExecFunction() internal { + m1.executionFunctions.push(AccountStateMutatingPlugin.executionFunction.selector); + _initMockPlugin(); + } + + function _initMockPluginPreAndPostExecutionHook() internal { + m1.executionHooks.push( + ManifestExecutionHook({ + executionSelector: AccountStateMutatingPlugin.executionFunction.selector, + preExecHook: ManifestFunction({ + functionType: ManifestAssociatedFunctionType.SELF, + functionId: _PRE_HOOK_FUNCTION_ID_1, + dependencyIndex: 0 // Unused + }), + postExecHook: ManifestFunction({ + functionType: ManifestAssociatedFunctionType.SELF, + functionId: _POST_HOOK_FUNCTION_ID_2, + dependencyIndex: 0 // Unused + }) + }) + ); + _initMockPlugin(); + } + + function _initMockPluginPostOnlyExecutionHook() internal { + m1.executionHooks.push( + ManifestExecutionHook({ + executionSelector: AccountStateMutatingPlugin.executionFunction.selector, + preExecHook: ManifestFunction({ + functionType: ManifestAssociatedFunctionType.NONE, + functionId: 0, // Unused + dependencyIndex: 0 // Unused + }), + postExecHook: ManifestFunction({ + functionType: ManifestAssociatedFunctionType.SELF, + functionId: _POST_HOOK_FUNCTION_ID_2, + dependencyIndex: 0 // Unused + }) + }) + ); + _initMockPlugin(); + } + + // Installs the account state mutating plugin. Prior to calling this, the test should configure the desired + // plugin functions and callbacks, since the manifest will change based on that configuration. + function _installASMPlugin() internal { + bytes32 manifestHash = _manifestHashOf(asmPlugin.pluginManifest()); + vm.expectEmit(true, true, true, true); + emit PluginInstalled(address(asmPlugin), manifestHash, _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS); + vm.prank(owner1); + account1.installPlugin(address(asmPlugin), manifestHash, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS); + } + + // Sets up the manifest hash variable and deploys the mock plugin. + function _initMockPlugin() internal { + manifestHash1 = _manifestHashOf(m1); + mockPlugin1 = new MockPlugin(m1); + } + + // Installs the mock plugin onto the account. Prior to calling this, the test should configure the desired + // plugin functions, and call _initMockPlugin() to set up the mock plugin. + function _installMockPlugin() internal { + vm.expectEmit(true, true, true, true); + emit ReceivedCall(abi.encodeCall(IPlugin.onInstall, (bytes(""))), 0); + vm.expectEmit(true, true, true, true); + emit PluginInstalled(address(mockPlugin1), manifestHash1, _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS); + vm.prank(owner1); + account1.installPlugin(address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS); + } + + function _manifestHashOf(PluginManifest memory manifest) internal pure returns (bytes32) { + return keccak256(abi.encode(manifest)); + } + + function _generateAndSignUserOp() internal view returns (UserOperation[] memory ops) { + ops = new UserOperation[](1); + ops[0] = UserOperation({ + sender: address(account1), + nonce: entryPoint.getNonce(address(account1), 0), + initCode: "", + callData: abi.encodeCall(AccountStateMutatingPlugin.executionFunction, ()), + callGasLimit: CALL_GAS_LIMIT, + verificationGasLimit: VERIFICATION_GAS_LIMIT, + preVerificationGas: 0, + maxFeePerGas: 2, + maxPriorityFeePerGas: 1, + paymasterAndData: "", + signature: "" + }); + + bytes32 userOpHash = entryPoint.getUserOpHash(ops[0]); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(owner1Key, userOpHash.toEthSignedMessageHash()); + ops[0].signature = abi.encodePacked(r, s, v); + } + + function _generateCallsUninstallASMInstallMock() internal view returns (Call[] memory) { + // Encode two self-calls: one to uninstall ASM plugin, one to install the mock plugin. + Call[] memory calls = new Call[](2); + calls[0] = Call({ + target: address(account1), + value: 0 ether, + data: abi.encodeCall(IPluginManager.uninstallPlugin, (address(asmPlugin), "", "", _EMPTY_HOOK_APPLY_DATA)) + }); + calls[1] = Call({ + target: address(account1), + value: 0 ether, + data: abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ) + }); + return calls; + } +} diff --git a/test/account/phases/AccountStatePhasesExec.t.sol b/test/account/phases/AccountStatePhasesExec.t.sol new file mode 100644 index 00000000..8d781f89 --- /dev/null +++ b/test/account/phases/AccountStatePhasesExec.t.sol @@ -0,0 +1,1309 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.21; + +import {AccountStatePhasesTest} from "./AccountStatePhases.t.sol"; + +import {IPluginManager} from "../../../src/interfaces/IPluginManager.sol"; +import { + IPlugin, + PluginManifest, + ManifestExecutionHook, + ManifestAssociatedFunction, + ManifestAssociatedFunctionType, + ManifestFunction +} from "../../../src/interfaces/IPlugin.sol"; +import {IStandardExecutor, Call} from "../../../src/interfaces/IStandardExecutor.sol"; + +import {AccountStateMutatingPlugin} from "../../mocks/plugins/AccountStateMutatingPlugin.sol"; +import {MockPlugin} from "../../mocks/MockPlugin.sol"; + +// Tests the account state phase behavior when the source of the state modification happens during execution. +contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { + // Test cases covered here + // These are listed in the order they are run in the test suite. + // The "source" indicates which in which phase the plugin will perform a modification, and the "target" + // indicates which phase will change as a result of the modification. + // + // - Source: pre-Exec + // - Target: pre-UserOp-Validation + // - n/a - runs before + // - Target: UserOp-Validation + // - n/a - runs before + // - Target: pre-Runtime-Validation + // - n/a - runs before + // - Target: Runtime-Validation + // - n/a - runs before + // - Target: pre-Exec (same phase) + // - Addition (first element): *impossible* + // - Addition (not first): should *not* run + // - Removal: should still run + // - Target: Exec (same phase) + // - Replace: original should run + // - Removal: original should run + // - Target: post-Exec (same phase) + // - Addition (associated, first pre-exec): *impossible* + // - Addition (associated, non-first pre-exec): should *not* run + // - Removal (associated, first pre-exec): *impossible* + // - Removal (associated, non-first pre-exec): should still run + // - Addition (first post-only): should *not* run + // - Addition (non-first post-only): should *not* run + // - Removal (first post-only): should still run + // - Removal (non-first post-only): should still run + // - Source: Exec + // - Target: pre-UserOp-Validation + // - n/a - runs before + // - Target: UserOp-Validation + // - n/a - runs before + // - Target: pre-Runtime-Validation + // - n/a - runs before + // - Target: Runtime-Validation + // - n/a - runs before + // - Target: pre-Exec (same phase) + // - n/a - runs before + // - Target: Exec (same phase) + // - Won’t test, since it’s the same single-element field. + // - Target: post-Exec (same phase) + // - Addition (associated, first pre-exec): should *not* run + // - Addition (associated, non-first pre-exec): should *not* run + // - Removal (associated, first pre-exec): should still run + // - Removal (associated, non-first pre-exec): should still run + // - Addition (first post-only): should *not* run + // - Addition (non-first post-only): should *not* run + // - Removal (first post-only): should still run + // - Removal (non-first post-only): should still run + // - Source: post-Exec + // - Target: pre-UserOp-Validation + // - n/a - runs before + // - Target: UserOp-Validation + // - n/a - runs before + // - Target: pre-Runtime-Validation + // - n/a - runs before + // - Target: Runtime-Validation + // - n/a - runs before + // - Target: pre-Exec (same phase) + // - n/a - runs before + // - Target: Exec (same phase) + // - n/a - runs before + // - Target: post-Exec (same phase) + // - Addition (associated, first pre-exec): should *not* run + // - Addition (associated, non-first pre-exec): should *not* run + // - Removal (associated, first pre-exec): should still run + // - Removal (associated, non-first pre-exec): should still run + // - Addition (first post-only): should *not* run + // - Addition (non-first post-only): should *not* run + // - Removal (first post-only): should still run + // - Removal (non-first post-only): should still run + + // Source: pre-Exec + // Target: pre-UserOp-Validation + // n/a - runs before + + // Source: pre-Exec + // Target: UserOp-Validation + // n/a - runs before + + // Source: pre-Exec + // Target: pre-Runtime-Validation + // n/a - runs before + + // Source: pre-Exec + // Target: Runtime-Validation + // n/a - runs before + + // Source: pre-Exec + // Target: pre-Exec (same phase) + // Addition (first element): *impossible* + + function test_ASP_preExec_add_preExec_notFirstElement() public { + // Source: pre-Exec + // Target: pre-Exec (same phase) + // Addition (not first): should *not* run + + // Set up the mock plugin with a pre-Exec hook, which will be added and should not run. + _initMockPluginPreExecutionHook(); + + // Install the ASM plugin with a pre exec hook that will add a pre exec hook. + // It also needs a pre exec hook to ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setPreExec: true, + setPostExec: false, + setRTValidation: false, + setPreRTValidation: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.PRE_EXECUTION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account by mocking a call from the EntryPoint, bypassing + // both user op and runtime validation. This will trigger the ASM plugin's pre exec hook to install the + // mock plugin's pre exec hook. + // Per the 6900 spec, because this is in the same phase, the state change should not be applied and the + // mock plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.preExecutionHook.selector), + 0 // Should be called 0 times + ); + vm.prank(address(entryPoint)); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_preExec_remove_preExec() public { + // Source: pre-Exec + // Target: pre-Exec (same phase) + // Removal: should still run + + // Set up the mock plugin with a pre-Exec hook, which will be removed and should still run. + _initMockPluginPreExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a pre exec hook that will remove the mock plugin's pre exec hook. + // It also needs a pre exec hook to ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setPreExec: true, + setPostExec: false, + setRTValidation: false, + setPreRTValidation: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.PRE_EXECUTION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account by mocking a call from the EntryPoint, bypassing + // both user op and runtime validation. This will trigger the ASM plugin's pre exec hook to remove the + // mock plugin's pre exec hook. + // Per the 6900 spec, because this is in the same phase, the state change should not be applied and the + // mock plugin's hook should still run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.preExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(address(entryPoint)); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_preExec_replace_exec() public { + // Source: pre-Exec + // Target: Exec (same phase) + // Replace: original should run + + // Set up the mock plugin with an Exec function, which will replace the one defined by the ASM plugin + // and should not be run. + _initMockPluginExecFunction(); + + // Install the ASM plugin with a pre exec hook that will replace the exec function. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setPreExec: true, + setPostExec: false, + setRTValidation: false, + setPreRTValidation: false + }); + // Encode two self-calls: one to uninstall ASM plugin, one to install the mock plugin. + Call[] memory calls = _generateCallsUninstallASMInstallMock(); + asmPlugin.setCallback( + abi.encodeCall(IStandardExecutor.executeBatch, (calls)), + AccountStateMutatingPlugin.FunctionId.PRE_EXECUTION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account by mocking a call from the EntryPoint, bypassing + // both user op and runtime validation. This will trigger the ASM plugin's pre exec hook to replace the + // exec function with the mock plugin's exec function. The original should run, not the replacement. + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(AccountStateMutatingPlugin.executionFunction.selector), + 1 // Should be called 1 time + ); + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(AccountStateMutatingPlugin.executionFunction.selector), + 0 // Should be called 0 times + ); + vm.prank(address(entryPoint)); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_preExec_remove_exec() public { + // Source: pre-Exec + // Target: Exec (same phase) + // Removal: original should run + + // Install the ASM plugin with a pre exec hook that will remove the exec function. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setPreExec: true, + setPostExec: false, + setRTValidation: false, + setPreRTValidation: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(asmPlugin), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.PRE_EXECUTION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account by mocking a call from the EntryPoint, bypassing + // both user op and runtime validation. This will trigger the ASM plugin's pre exec hook to remove the + // exec function. This NOT cause the call to revert, due to being in the same phase. + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(AccountStateMutatingPlugin.executionFunction.selector), + 1 // Should be called 1 time + ); + vm.prank(address(entryPoint)); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + // Source: pre-Exec + // Target: post-Exec (same phase) + // Addition (associated, first pre-exec): *impossible* + + function test_ASP_preExec_add_postExec_associated_notFirstElement() public { + // Source: pre-Exec + // Target: post-Exec (same phase) + // Addition (associated, non-first pre-exec): should *not* run + + // Set up the mock plugin with an associated post-Exec hook, which will be added and should not run. + _initMockPluginPreAndPostExecutionHook(); + + // Install the ASM plugin with a pre exec hook that will add a post exec hook. + // It also needs a pre exec hook to ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setPreExec: true, + setPostExec: false, + setRTValidation: false, + setPreRTValidation: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.PRE_EXECUTION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account by mocking a call from the EntryPoint, bypassing + // both user op and runtime validation. This will trigger the ASM plugin's pre exec hook to install the + // mock plugin's post exec hook. + // Per the 6900 spec, because this is in the same phase, the state change should not be applied and the + // mock plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + vm.prank(address(entryPoint)); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + // Source: pre-Exec + // Target: post-Exec (same phase) + // Removal (associated, first pre-exec): *impossible* + + function test_ASP_preExec_remove_postExec_associated_notFirstElement() public { + // Source: pre-Exec + // Target: post-Exec (same phase) + // Removal (associated, non-first pre-exec): should still run + + // Set up the mock plugin with an associated post-Exec hook, which will be removed and should still run. + _initMockPluginPreAndPostExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a pre exec hook that will remove the mock plugin's post exec hook. + // It also needs a pre exec hook to ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setPreExec: true, + setPostExec: false, + setRTValidation: false, + setPreRTValidation: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.PRE_EXECUTION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account by mocking a call from the EntryPoint, bypassing + // both user op and runtime validation. This will trigger the ASM plugin's pre exec hook to remove the + // mock plugin's post exec hook. + // Per the 6900 spec, because this is in the same phase, the state change should not be applied and the + // mock plugin's hook should still run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(address(entryPoint)); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_preExec_add_postExec_firstElement() public { + // Source: pre-Exec + // Target: post-Exec (same phase) + // Addition (first post-only): should *not* run + + // Set up the mock plugin with a post-Exec hook, which will be added and should not run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the ASM plugin with a pre exec hook that will add a post exec hook. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setPreExec: true, + setPostExec: false, + setRTValidation: false, + setPreRTValidation: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.PRE_EXECUTION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account by mocking a call from the EntryPoint, bypassing + // both user op and runtime validation. This will trigger the ASM plugin's pre exec hook to install the + // mock plugin's post exec hook. + // Per the 6900 spec, because this is in the same phase, the state change should not be applied and the + // mock plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + vm.prank(address(entryPoint)); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_preExec_add_postExec_notFirstElement() public { + // Source: pre-Exec + // Target: post-Exec (same phase) + // Addition (non-first post-only): should *not* run + + // Set up the mock plugin with a post-Exec hook, which will be added and should not run. + _initMockPluginPostOnlyExecutionHook(); + + // Since the ASM plugin can't define a post-only hook due to using a pre exec hook for its action, we + // need to add another mock plugin to add the first post-only exec hook, in order to test this case. + + PluginManifest memory m2; + m2.executionHooks = new ManifestExecutionHook[](1); + m2.executionHooks[0] = ManifestExecutionHook({ + executionSelector: AccountStateMutatingPlugin.executionFunction.selector, + preExecHook: ManifestFunction({ + functionType: ManifestAssociatedFunctionType.NONE, + functionId: 0, // Unused + dependencyIndex: 0 // Unused + }), + postExecHook: ManifestFunction({ + functionType: ManifestAssociatedFunctionType.SELF, + functionId: _POST_HOOK_FUNCTION_ID_2, + dependencyIndex: 0 // Unused + }) + }); + bytes32 manifestHash2 = _manifestHashOf(m2); + MockPlugin mockPlugin2 = new MockPlugin(m2); + + vm.expectEmit(true, true, true, true); + emit ReceivedCall(abi.encodeCall(IPlugin.onInstall, (bytes(""))), 0); + vm.expectEmit(true, true, true, true); + emit PluginInstalled(address(mockPlugin2), manifestHash2, _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS); + vm.prank(owner1); + account1.installPlugin(address(mockPlugin2), manifestHash2, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS); + + // Install the ASM plugin with a pre exec hook that will add a post exec hook. + // It also needs a post-only exec hook to ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setPreExec: true, + setPostExec: true, + setRTValidation: false, + setPreRTValidation: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.PRE_EXECUTION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account by mocking a call from the EntryPoint, bypassing + // both user op and runtime validation. This will trigger the ASM plugin's pre exec hook to install the + // mock plugin's post exec hook. + // Per the 6900 spec, because this is in the same phase, the state change should not be applied and the + // mock plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + vm.expectCall( + address(mockPlugin2), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(address(entryPoint)); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_preExec_remove_postExec_firstElement() public { + // Source: pre-Exec + // Target: post-Exec (same phase) + // Removal (first post-only): should still run + + // Set up the mock plugin with a post-Exec hook, which will be removed and should still run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a pre exec hook that will remove the mock plugin's post exec hook. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setPreExec: true, + setPostExec: false, + setRTValidation: false, + setPreRTValidation: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.PRE_EXECUTION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account by mocking a call from the EntryPoint, bypassing + // both user op and runtime validation. This will trigger the ASM plugin's pre exec hook to remove the + // mock plugin's post exec hook. + // Per the 6900 spec, because this is in the same phase, the state change should not be applied and the + // mock plugin's hook should still run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(address(entryPoint)); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_preExec_remove_postExec_notFirstElement() public { + // Source: pre-Exec + // Target: post-Exec (same phase) + // Removal (non-first post-only): should still run + + // Since the ASM plugin can't define a post-only hook due to using a pre exec hook for its action, we + // need to add another mock plugin to add the first post-only exec hook, in order to test this case. + + PluginManifest memory m2; + m2.executionHooks = new ManifestExecutionHook[](1); + m2.executionHooks[0] = ManifestExecutionHook({ + executionSelector: AccountStateMutatingPlugin.executionFunction.selector, + preExecHook: ManifestFunction({ + functionType: ManifestAssociatedFunctionType.NONE, + functionId: 0, // Unused + dependencyIndex: 0 // Unused + }), + postExecHook: ManifestFunction({ + functionType: ManifestAssociatedFunctionType.SELF, + functionId: _POST_HOOK_FUNCTION_ID_2, + dependencyIndex: 0 // Unused + }) + }); + bytes32 manifestHash2 = _manifestHashOf(m2); + MockPlugin mockPlugin2 = new MockPlugin(m2); + + vm.expectEmit(true, true, true, true); + emit ReceivedCall(abi.encodeCall(IPlugin.onInstall, (bytes(""))), 0); + vm.expectEmit(true, true, true, true); + emit PluginInstalled(address(mockPlugin2), manifestHash2, _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS); + vm.prank(owner1); + account1.installPlugin(address(mockPlugin2), manifestHash2, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS); + + // Set up the mock plugin with a post-Exec hook, which will be removed and should still run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a pre exec hook that will remove the mock plugin's post exec hook. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setPreExec: true, + setPostExec: false, + setRTValidation: false, + setPreRTValidation: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.PRE_EXECUTION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account by mocking a call from the EntryPoint, bypassing + // both user op and runtime validation. This will trigger the ASM plugin's pre exec hook to remove the + // mock plugin's post exec hook. + // Per the 6900 spec, because this is in the same phase, the state change should not be applied and the + // mock plugin's hook should still run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.expectCall( + address(mockPlugin2), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(address(entryPoint)); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + // Source: Exec + // Target: pre-UserOp-Validation + // n/a - runs before + + // Source: Exec + // Target: UserOp-Validation + // n/a - runs before + + // Source: Exec + // Target: pre-Runtime-Validation + // n/a - runs before + + // Source: Exec + // Target: Runtime-Validation + // n/a - runs before + + // Source: Exec + // Target: pre-Exec (same phase) + // n/a - runs before + + // Source: Exec + // Target: Exec (same phase) + // Won’t test, since it’s the same single-element field. + + function test_ASP_exec_add_postExec_associated_firstElement() public { + // Source: Exec + // Target: post-Exec (same phase) + // Addition (associated, first pre-exec): should *not* run + + // Set up the mock plugin with an associated post-Exec hook, which will be added and should not run. + _initMockPluginPreAndPostExecutionHook(); + + // Install the ASM plugin with an exec function that will add a post exec hook. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setPreExec: false, + setPostExec: false, + setRTValidation: false, + setPreRTValidation: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.EXECUTION_FUNCTION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account by mocking a call from the EntryPoint, bypassing + // user op and runtime validation. This will trigger the ASM plugin's exec function to install the + // mock plugin's post exec hook. + // Per the 6900 spec, because this is in the same phase, the state change should not be applied and the + // mock plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + vm.prank(address(entryPoint)); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_exec_add_postExec_associated_notFirstElement() public { + // Source: Exec + // Target: post-Exec (same phase) + // Addition (associated, non-first pre-exec): should *not* run + + // Set up the mock plugin with an associated post-Exec hook, which will be added and should not run. + _initMockPluginPreAndPostExecutionHook(); + + // Install the ASM plugin with an exec function that will add a post exec hook. + // It also needs a post exec hook to ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setPreExec: true, + setPostExec: true, + setRTValidation: false, + setPreRTValidation: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.EXECUTION_FUNCTION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account by mocking a call from the EntryPoint, bypassing + // user op and runtime validation. This will trigger the ASM plugin's exec function to install the + // mock plugin's post exec hook. + // Per the 6900 spec, because this is in the same phase, the state change should not be applied and the + // mock plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(address(entryPoint)); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_exec_remove_postExec_associated_firstElement() public { + // Source: Exec + // Target: post-Exec (same phase) + // Removal (associated, first pre-exec): should still run + + // Set up the mock plugin with an associated post-Exec hook, which will be removed and should still run. + _initMockPluginPreAndPostExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with an exec function that will remove the mock plugin's post exec hook. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setPreExec: false, + setPostExec: false, + setRTValidation: false, + setPreRTValidation: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.EXECUTION_FUNCTION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account by mocking a call from the EntryPoint, bypassing + // user op and runtime validation. This will trigger the ASM plugin's exec function to remove the + // mock plugin's post exec hook. + // Per the 6900 spec, because this is in the same phase, the state change should not be applied and the + // mock plugin's hook should still run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(address(entryPoint)); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_exec_remove_postExec_associated_notFirstElement() public { + // Source: Exec + // Target: post-Exec (same phase) + // Removal (associated, non-first pre-exec): should still run + + // Set up the mock plugin with an associated post-Exec hook, which will be removed and should still run. + _initMockPluginPreAndPostExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with an exec function that will remove the mock plugin's post exec hook. + // It also needs an associated post exec hook to ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setPreExec: true, + setPostExec: true, + setRTValidation: false, + setPreRTValidation: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.EXECUTION_FUNCTION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account by mocking a call from the EntryPoint, bypassing + // user op and runtime validation. This will trigger the ASM plugin's exec function to remove the + // mock plugin's post exec hook. + // Per the 6900 spec, because this is in the same phase, the state change should not be applied and the + // mock plugin's hook should still run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(address(entryPoint)); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_exec_add_postExec_firstElement() public { + // Source: Exec + // Target: post-Exec (same phase) + // Addition (first post-only): should *not* run + + // Set up the mock plugin with a post-Exec hook, which will be added and should not run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the ASM plugin with an exec function that will add a post exec hook. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setPreExec: false, + setPostExec: false, + setRTValidation: false, + setPreRTValidation: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.EXECUTION_FUNCTION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account by mocking a call from the EntryPoint, bypassing + // user op and runtime validation. This will trigger the ASM plugin's exec function to install the + // mock plugin's post exec hook. + // Per the 6900 spec, because this is in the same phase, the state change should not be applied and the + // mock plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + vm.prank(address(entryPoint)); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_exec_add_postExec_notFirstElement() public { + // Source: Exec + // Target: post-Exec (same phase) + // Addition (non-first post-only): should *not* run + + // Set up the mock plugin with a post-Exec hook, which will be added and should not run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the ASM plugin with an exec function that will add a post exec hook. + // It also needs a post exec hook to ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setPreExec: false, + setPostExec: true, + setRTValidation: false, + setPreRTValidation: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.EXECUTION_FUNCTION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account by mocking a call from the EntryPoint, bypassing + // user op and runtime validation. This will trigger the ASM plugin's exec function to install the + // mock plugin's post exec hook. + // Per the 6900 spec, because this is in the same phase, the state change should not be applied and the + // mock plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(address(entryPoint)); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_exec_remove_postExec_firstElement() public { + // Source: Exec + // Target: post-Exec (same phase) + // Removal (first post-only): should still run + + // Set up the mock plugin with a post-Exec hook, which will be removed and should still run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with an exec function that will remove the mock plugin's post exec hook. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setPreExec: false, + setPostExec: false, + setRTValidation: false, + setPreRTValidation: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.EXECUTION_FUNCTION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account by mocking a call from the EntryPoint, bypassing + // user op and runtime validation. This will trigger the ASM plugin's exec function to remove the + // mock plugin's post exec hook. + // Per the 6900 spec, because this is in the same phase, the state change should not be applied and the + // mock plugin's hook should still run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(address(entryPoint)); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_exec_remove_postExec_notFirstElement() public { + // Source: Exec + // Target: post-Exec (same phase) + // Removal (non-first post-only): should still run + + // Set up the mock plugin with a post-Exec hook, which will be removed and should still run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with an exec function that will remove the mock plugin's post exec hook. + // It also needs a post exec hook to ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setPreExec: false, + setPostExec: true, + setRTValidation: false, + setPreRTValidation: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.EXECUTION_FUNCTION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account by mocking a call from the EntryPoint, bypassing + // user op and runtime validation. This will trigger the ASM plugin's exec function to remove the + // mock plugin's post exec hook. + // Per the 6900 spec, because this is in the same phase, the state change should not be applied and the + // mock plugin's hook should still run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(address(entryPoint)); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + // Source: post-Exec + // Target: pre-UserOp-Validation + // n/a - runs before + + // Source: post-Exec + // Target: UserOp-Validation + // n/a - runs before + + // Source: post-Exec + // Target: pre-Runtime-Validation + // n/a - runs before + + // Source: post-Exec + // Target: Runtime-Validation + // n/a - runs before + + // Source: post-Exec + // Target: pre-Exec + // n/a - runs before + + // Source: post-Exec + // Target: Exec + // n/a - runs before + + // Source: post-Exec + // Target: post-Exec (same phase) + // Addition (associated, first pre-exec): impossible with the current order of running post-only exec hooks + // after associated post hooks. + + function test_ASP_postExec_add_postExec_associated_notFirstElement() public { + // Source: post-Exec + // Target: post-Exec (same phase) + // Addition (associated, non-first pre-exec): should *not* run + + // Set up the mock plugin with an associated post-Exec hook, which will be added and should not run. + _initMockPluginPreAndPostExecutionHook(); + + // Install the ASM plugin with a post exec hook that will add a post exec hook. + // To ensure the ASM plugin's post exec hook runs first, it needs to be associated, so we also define an + // empty pre-exec hook. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setPreExec: true, + setPostExec: true, + setRTValidation: false, + setPreRTValidation: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.POST_EXECUTION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account by mocking a call from the EntryPoint, bypassing + // user op and runtime validation. This will trigger the ASM plugin's post exec hook to install the + // mock plugin's post exec hook. + // Per the 6900 spec, because this is in the same phase, the state change should not be applied and the + // mock plugin's hook should not run. + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + vm.prank(address(entryPoint)); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + // Source: post-Exec + // Target: post-Exec (same phase) + // Removal (associated, first pre-exec): impossible with the current order of running post-only exec hooks + // after associated post hooks. + + function test_ASP_postExec_remove_postExec_associated_notFirstElement() public { + // Source: post-Exec + // Target: post-Exec (same phase) + // Removal (associated, non-first pre-exec): should still run + + // Set up the mock plugin with an associated post-Exec hook, which will be removed and should still run. + _initMockPluginPreAndPostExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a post exec hook that will remove the mock plugin's post exec hook. + // To ensure the ASM plugin's post exec hook runs first, it needs to be associated, so we also define an + // empty pre-exec hook. This also ensures it is not the first post exec hook. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setPreExec: true, + setPostExec: true, + setRTValidation: false, + setPreRTValidation: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.POST_EXECUTION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account by mocking a call from the EntryPoint, bypassing + // user op and runtime validation. This will trigger the ASM plugin's post exec hook to remove the + // mock plugin's post exec hook. + // Per the 6900 spec, because this is in the same phase, the state change should not be applied and the + // ASM plugin's hook should still run. + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(address(entryPoint)); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_postExec_add_postExec_firstElement() public { + // Source: post-Exec + // Target: post-Exec (same phase) + // Addition (first post-only): should *not* run + + // Set up the mock plugin with a post-Exec hook, which will be added and should not run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the ASM plugin with a post exec hook that will add a post exec hook. + // To ensure the ASM plugin's post exec hook runs first, it needs to be associated, so we also define an + // empty pre-exec hook. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setPreExec: true, + setPostExec: true, + setRTValidation: false, + setPreRTValidation: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.POST_EXECUTION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account by mocking a call from the EntryPoint, bypassing + // user op and runtime validation. This will trigger the ASM plugin's post exec hook to install the + // mock plugin's post exec hook. + // Per the 6900 spec, because this is in the same phase, the state change should not be applied and the + // ASM plugin's hook should not run. + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + vm.prank(address(entryPoint)); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_postExec_add_postExec_notFirstElement() public { + // Source: post-Exec + // Target: post-Exec (same phase) + // Addition (non-first post-only): should *not* run + + // Set up the mock plugin with a post-Exec hook, which will be added and should not run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the ASM plugin with a post exec hook that will add a post exec hook. + // This will be a post-only hook. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setPreExec: false, + setPostExec: true, + setRTValidation: false, + setPreRTValidation: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.POST_EXECUTION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account by mocking a call from the EntryPoint, bypassing + // user op and runtime validation. This will trigger the ASM plugin's post exec hook to install the + // mock plugin's post exec hook. + // Per the 6900 spec, because this is in the same phase, the state change should not be applied and the + // ASM plugin's hook should not run. + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + vm.prank(address(entryPoint)); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_postExec_remove_postExec_firstElement() public { + // Source: post-Exec + // Target: post-Exec (same phase) + // Removal (first post-only): should still run + + // Set up the mock plugin with a post-Exec hook, which will be removed and should still run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a post exec hook that will remove the mock plugin's post exec hook. + // To ensure the ASM plugin's post exec hook runs first, it needs to be associated, so we also define an + // empty pre-exec hook. This also ensures it is not the first post exec hook. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setPreExec: true, + setPostExec: true, + setRTValidation: false, + setPreRTValidation: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.POST_EXECUTION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account by mocking a call from the EntryPoint, bypassing + // user op and runtime validation. This will trigger the ASM plugin's post exec hook to remove the + // mock plugin's post exec hook. + // Per the 6900 spec, because this is in the same phase, the state change should not be applied and the + // ASM plugin's hook should still run. + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(address(entryPoint)); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_postExec_remove_postExec_notFirstElement() public { + // Source: post-Exec + // Target: post-Exec (same phase) + // Removal (non-first post-only): should still run + + // Set up the mock plugin with a post-Exec hook, which will be removed and should still run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a post exec hook that will remove the mock plugin's post exec hook. + // This will be a post-only hook. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setPreExec: false, + setPostExec: true, + setRTValidation: false, + setPreRTValidation: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.POST_EXECUTION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account by mocking a call from the EntryPoint, bypassing + // user op and runtime validation. This will trigger the ASM plugin's post exec hook to remove the + // mock plugin's post exec hook. + // Per the 6900 spec, because this is in the same phase, the state change should not be applied and the + // mock plugin's hook should still run. + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(address(entryPoint)); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } +} diff --git a/test/account/phases/AccountStatePhasesRTValidation.t.sol b/test/account/phases/AccountStatePhasesRTValidation.t.sol new file mode 100644 index 00000000..521ea0aa --- /dev/null +++ b/test/account/phases/AccountStatePhasesRTValidation.t.sol @@ -0,0 +1,1387 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.21; + +import {AccountStatePhasesTest} from "./AccountStatePhases.t.sol"; + +import {IPluginManager} from "../../../src/interfaces/IPluginManager.sol"; +import {IPlugin} from "../../../src/interfaces/IPlugin.sol"; +import {IStandardExecutor, Call} from "../../../src/interfaces/IStandardExecutor.sol"; +import {UpgradeableModularAccount} from "../../../src/account/UpgradeableModularAccount.sol"; + +import {AccountStateMutatingPlugin} from "../../mocks/plugins/AccountStateMutatingPlugin.sol"; + +// Tests the account state phase behavior when the source of the state modification +// happens during runtime validation. +contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { + // Test cases covered here: + // These are listed in the order they are run in the test suite. + // The "source" indicates which in which phase the plugin will perform a modification, and the "target" + // indicates which phase will change as a result of the modification. + // + // - Source: pre-Runtime-Validation + // - Target: pre-UserOp-Validation + // - n/a - can’t run in the same call + // - Target: UserOp-Validation + // - n/a - can’t run in the same call + // - Target: pre-Runtime-Validation (same phase) + // - Addition: adding a hook should not result in that hook running. + // - Removal: removing a hook should still have the hook run. + // - Target: Runtime-Validation (same phase) + // - Replace: original should run + // - Removal: original should run + // - Target: pre-Exec (different phase) + // - Addition (first element): should run + // - Addition (not first): should run + // - Removal: should *not* run + // - Target: Exec (different phase) + // - Replace: replacement should run + // - Removal: should revert as empty + // - Target: post-Exec (different phase) + // - Addition (associated, first pre-exec): should run + // - Addition (associated, non-first pre-exec): should run + // - Removal (associated, first pre-exec): should *not* run + // - Removal (associated, non-first pre-exec): should *not* run + // - Addition (first post-only): should run + // - Addition (non-first post-only): should run + // - Removal (first post-only): should *not* run + // - Removal (non-first post-only): should *not* run + // - Source: Runtime-Validation + // - Target: pre-UserOp-Validation + // - n/a - can’t run in the same call + // - Target: UserOp-Validation + // - n/a - can’t run in the same call + // - Target: pre-Runtime-Validation (same phase) + // - n/a - runs before + // - Target: Runtime-Validation (same phase) + // - Won’t test, since it’s the same single-element field. + // - Target: pre-Exec (different phase) + // - Addition (first element): should run + // - Addition (not first): should run + // - Removal: should *not* run + // - Target: Exec (different phase) + // - Replace: replacement should run + // - Removal: should revert as empty + // - Target: post-Exec (different phase) + // - Addition (associated, first pre-exec): should run + // - Addition (associated, non-first pre-exec): should run + // - Removal (associated, first pre-exec): should *not* run + // - Removal (associated, non-first pre-exec): should *not* run + // - Addition (first post-only): should run + // - Addition (non-first post-only): should run + // - Removal (first post-only): should *not* run + // - Removal (non-first post-only): should *not* run + + // Source: pre-Runtime-Validation + // Target: pre-UserOp-Validation + // n/a - can’t run in the same call + + // Source: pre-Runtime-Validation + // Target: UserOp-Validation + // n/a - can’t run in the same call + + function test_ASP_preRTValidation_add_preRTValidation() public { + // Source: pre-Runtime-Validation + // Target: pre-Runtime-Validation (same phase) + // Addition: adding a hook should not result in that hook running. + + // Set up the mock plugin with a pre-Runtime-Validation hook, which will be added and should not run. + _initMockPluginPreRuntimeValidationHook(); + + // Install the ASM plugin with a pre runtime validation hook that will add a pre runtime validation + // hook. + // Runtime validation is also needed to allow the call to be performed. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: true, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger the + // ASM + // plugin's pre runtime validation function to install the mock plugin's pre runtime validation hook. + // Per the 6900 spec, because this is in the same phase, the state change should not be applied and the + // mock + // plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(AccountStateMutatingPlugin.preRuntimeValidationHook.selector), + 0 // Should be called 0 times + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_preRTValidation_remove_preRTValidation() public { + // Source: pre-Runtime-Validation + // Target: pre-Runtime-Validation (same phase) + // Removal: removing a hook should still have the hook run. + + // Set up the mock plugin with a pre-Runtime-Validation hook, which will be removed and should run. + _initMockPluginPreRuntimeValidationHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a pre runtime validation hook that will remove the mock plugin's pre + // runtime validation hook. + // Runtime validation is also needed to allow the call to be performed. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: true, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger the + // ASM plugin's pre runtime validation function to remove the mock plugin's pre runtime validation hook. + // Per the 6900 spec, because this is in the same phase, the state change should not be applied and the + // mock plugin's hook should run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(AccountStateMutatingPlugin.preRuntimeValidationHook.selector), + 1 // Should be called 1 time + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_preRTValidation_replace_RTValidation() public { + // Source: pre-Runtime-Validation + // Target: Runtime-Validation (same phase) + // Replace: original should run + + // Set up the mock plugin with a Runtime-Validation function, which will replace the one defined by the + // ASM plugin and should not be run. + // To allow the call to complete as intended, we also add the execution function to the mock plugin. + m1.executionFunctions.push(AccountStateMutatingPlugin.executionFunction.selector); + _initMockPluginRuntimeValidationFunction(); + + // Install the ASM plugin with a pre runtime validation hook that will replace the runtime validation + // function. + // Runtime validation is also needed to allow the call to be performed. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: true, + setPreExec: false, + setPostExec: false + }); + // Encode two self-calls: one to uninstall ASM plugin, one to install the mock plugin. + Call[] memory calls = _generateCallsUninstallASMInstallMock(); + asmPlugin.setCallback( + abi.encodeCall(IStandardExecutor.executeBatch, (calls)), + AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger the + // ASM plugin's pre runtime validation function to replace the runtime validation function with the mock + // plugin's runtime validation function. The original should run, not the replacement. + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(AccountStateMutatingPlugin.runtimeValidationFunction.selector), + 1 // Should be called 1 time + ); + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(AccountStateMutatingPlugin.runtimeValidationFunction.selector), + 0 // Should be called 0 times + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_preRTValidation_remove_RTValidation() public { + // Source: pre-Runtime-Validation + // Target: Runtime-Validation (same phase) + // Removal: original should run + + // To allow the exec call to not revert, we add the execution function to the mock plugin. + _initMockPluginExecFunction(); + + // Install the ASM plugin with a pre runtime validation hook that will remove the runtime validation + // function. + // Runtime validation is also needed to allow the call to be performed. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: true, + setPreExec: false, + setPostExec: false + }); + Call[] memory calls = _generateCallsUninstallASMInstallMock(); + asmPlugin.setCallback( + abi.encodeCall(IStandardExecutor.executeBatch, (calls)), + AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger the + // ASM plugin's pre runtime validation function to remove the runtime validation function. The original + // runtime validation function should run. + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(AccountStateMutatingPlugin.runtimeValidationFunction.selector), + 1 // Should be called 1 time + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_preRTValidation_add_preExec_firstElement() public { + // Source: pre-Runtime-Validation + // Target: pre-Exec (different phase) + // Addition (first element): should run + + // Set up the mock plugin with a pre-Exec hook, which will be added and should run. + _initMockPluginPreExecutionHook(); + + // Install the ASM plugin with a pre runtime validation hook that will add a pre exec hook. + // It also needs a runtime validation function to allow the call to be performed. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: true, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger the + // ASM plugin's pre runtime validation function to install the mock plugin's pre exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the + // mock plugin's hook should run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.preExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_preRTValidation_add_preExec_notFirstElement() public { + // Source: pre-Runtime-Validation + // Target: pre-Exec (different phase) + // Addition (not first): should run + + // Set up the mock plugin with a pre-Exec hook, which will be added and should run. + _initMockPluginPreExecutionHook(); + + // Install the ASM plugin with a pre runtime validation hook that will add a pre exec hook. + // It also needs a runtime validation function to allow the call to be performed, and a pre exec hook to + // ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: true, + setPreExec: true, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger the + // ASM plugin's pre runtime validation function to install the mock plugin's pre exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the + // mock plugin's hook should run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.preExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.preExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_preRTValidation_remove_preExec() public { + // Source: pre-Runtime-Validation + // Target: pre-Exec (different phase) + // Removal: should *not* run + + // Set up the mock plugin with a pre-Exec hook, which will be removed and should not run. + _initMockPluginPreExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a pre runtime validation hook that will remove the mock plugin's pre exec + // hook. + // It also needs a runtime validation function to allow the call to be performed. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: true, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger the + // ASM plugin's pre runtime validation function to remove the mock plugin's pre exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the + // mock plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.preExecutionHook.selector), + 0 // Should be called 0 times + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_preRTValidation_replace_exec() public { + // Source: pre-Runtime-Validation + // Target: Exec (different phase) + // Replace: replacement should run + + // Set up the mock plugin with an Exec function, which will replace the one defined by the ASM plugin + // and should be run. + _initMockPluginExecFunction(); + + // Install the ASM plugin with a pre runtime validation hook that will replace the exec function. + // Runtime validation is also needed to allow the call to be performed. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: true, + setPreExec: false, + setPostExec: false + }); + // Encode two self-calls: one to uninstall ASM plugin, one to install the mock plugin. + Call[] memory calls = _generateCallsUninstallASMInstallMock(); + asmPlugin.setCallback( + abi.encodeCall(IStandardExecutor.executeBatch, (calls)), + AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger the + // ASM plugin's pre runtime validation function to replace the exec function with the mock plugin's exec + // function. The replacement should run, not the original. + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(AccountStateMutatingPlugin.executionFunction.selector), + 0 // Should be called 0 times + ); + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(AccountStateMutatingPlugin.executionFunction.selector), + 1 // Should be called 1 time + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_preRTValidation_remove_exec() public { + // Source: pre-Runtime-Validation + // Target: Exec (different phase) + // Removal: should revert as empty + + // Install the ASM plugin with a pre runtime validation hook that will remove the exec function. + // Runtime validation is also needed to allow the call to be performed. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: true, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(asmPlugin), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger the + // ASM plugin's pre runtime validation function to remove the exec function. This should cause the call to + // revert, but only after the ASM plugin's runtime validation function has run. + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.runtimeValidationFunction.selector), + 1 // Should be called 1 time + ); + vm.expectRevert( + abi.encodeWithSelector( + UpgradeableModularAccount.UnrecognizedFunction.selector, + AccountStateMutatingPlugin.executionFunction.selector + ) + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_preRTValidation_add_postExec_associated_firstElement() public { + // Source: pre-Runtime-Validation + // Target: post-Exec (different phase) + // Addition (associated, first pre-exec): should run + + // Set up the mock plugin with an associated post-Exec hook, which will be added and should run. + _initMockPluginPreAndPostExecutionHook(); + + // Install the ASM plugin with a pre runtime validation hook that will add a post exec hook. + // It also needs a runtime validation function to allow the call to be performed. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: true, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger the + // ASM plugin's pre runtime validation function to install the mock plugin's post exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the + // mock plugin's hook should run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_preRTValidation_add_postExec_associated_notFirstElement() public { + // Source: pre-Runtime-Validation + // Target: post-Exec (different phase) + // Addition (associated, non-first pre-exec): should run + + // Set up the mock plugin with an associated post-Exec hook, which will be added and should run. + _initMockPluginPreAndPostExecutionHook(); + + // Install the ASM plugin with a pre runtime validation hook that will add a post exec hook. + // It also needs a runtime validation function to allow the call to be performed, and a pre exec hook to + // ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: true, + setPreExec: true, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger the + // ASM plugin's pre runtime validation function to install the mock plugin's post exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the + // mock plugin's hook should run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_preRTValidation_remove_postExec_associated_firstElement() public { + // Source: pre-Runtime-Validation + // Target: post-Exec (different phase) + // Removal (associated, first pre-exec): should *not* run + + // Set up the mock plugin with an associated post-Exec hook, which will be removed and should not run. + _initMockPluginPreAndPostExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a pre runtime validation hook that will remove the mock plugin's post exec + // hook. + // It also needs a runtime validation function to allow the call to be performed. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: true, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger the + // ASM plugin's pre runtime validation function to remove the mock plugin's post exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the + // mock plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_preRTValidation_remove_postExec_associated_notFirstElement() public { + // Source: pre-Runtime-Validation + // Target: post-Exec (different phase) + // Removal (associated, non-first pre-exec): should *not* run + + // Set up the mock plugin with an associated post-Exec hook, which will be removed and should not run. + _initMockPluginPreAndPostExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a pre runtime validation hook that will remove the mock plugin's post exec + // hook. It also needs a runtime validation function to allow the call to be performed, and a pre exec + // hook to ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: true, + setPreExec: true, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger the + // ASM plugin's pre runtime validation function to remove the mock plugin's post exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the + // mock plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_preRTValidation_add_postExec_firstElement() public { + // Source: pre-Runtime-Validation + // Target: post-Exec (different phase) + // Addition (first post-only): should run + + // Set up the mock plugin with a post-Exec hook, which will be added and should run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the ASM plugin with a pre runtime validation hook that will add a post exec hook. + // It also needs a runtime validation function to allow the call to be performed. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: true, + setPostExec: false, + setPreExec: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger the + // ASM plugin's pre runtime validation function to install the mock plugin's post exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the + // mock plugin's hook should run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_preRTValidation_add_postExec_notFirstElement() public { + // Source: pre-Runtime-Validation + // Target: post-Exec (different phase) + // Addition (non-first post-only): should run + + // Set up the mock plugin with a post-Exec hook, which will be added and should run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the ASM plugin with a pre runtime validation hook that will add a post exec hook. + // It also needs a runtime validation function to allow the call to be performed, and a post-only exec hook + // to ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: true, + setPostExec: true, + setPreExec: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger the + // ASM plugin's pre runtime validation function to install the mock plugin's post exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the + // mock plugin's hook should run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_preRTValidation_remove_postExec_firstElement() public { + // Source: pre-Runtime-Validation + // Target: post-Exec (different phase) + // Removal (first post-only): should *not* run + + // Set up the mock plugin with a post-Exec hook, which will be removed and should not run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a pre runtime validation hook that will remove the mock plugin's post exec + // hook. + // It also needs a runtime validation function to allow the call to be performed. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: true, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger the + // ASM plugin's pre runtime validation function to remove the mock plugin's post exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the + // mock plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_preRTValidation_remove_postExec_notFirstElement() public { + // Source: pre-Runtime-Validation + // Target: post-Exec (different phase) + // Removal (non-first post-only): should *not* run + + // Set up the mock plugin with a post-Exec hook, which will be removed and should not run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a pre runtime validation hook that will remove the mock plugin's post exec + // hook. It also needs a runtime validation function to allow the call to be performed, and a post-only + // exec hook to ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: true, + setPostExec: true, + setPreExec: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger the + // ASM plugin's pre runtime validation function to remove the mock plugin's post exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the + // mock plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + // Source: Runtime-Validation + // Target: pre-UserOp-Validation + // n/a - can’t run in the same call + + // Source: Runtime-Validation + // Target: UserOp-Validation + // n/a - can’t run in the same call + + // Source: Runtime-Validation + // Target: pre-Runtime-Validation (same phase) + // n/a - runs before + + // Source: Runtime-Validation + // Target: Runtime-Validation (same phase) + // Won’t test, since it’s the same single-element field. + + function test_ASP_RTValidation_add_preExec_firstElement() public { + // Source: Runtime-Validation + // Target: pre-Exec (different phase) + // Addition (first element): should run + + // Set up the mock plugin with a pre-Exec hook, which will be added and should run. + _initMockPluginPreExecutionHook(); + + // Install the ASM plugin with a runtime validation function that will add a pre exec hook. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: false, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.RUNTIME_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger the + // mock plugin's runtime validation function to install the mock plugin's pre exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the + // mock plugin's hook should run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.preExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_RTValidation_add_preExec_notFirstElement() public { + // Source: Runtime-Validation + // Target: pre-Exec (different phase) + // Addition (not first): should run + + // Set up the mock plugin with a pre-Exec hook, which will be added and should run. + _initMockPluginPreExecutionHook(); + + // Install the ASM plugin with a runtime validation function that will add a pre exec hook. + // It also needs a pre exec hook to ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: false, + setPreExec: true, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.RUNTIME_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger the + // mock plugin's runtime validation function to install the mock plugin's pre exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the + // mock plugin's hook should run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.preExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.preExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_RTValidation_remove_preExec() public { + // Source: Runtime-Validation + // Target: pre-Exec (different phase) + // Removal: should *not* run + + // Set up the mock plugin with a pre-Exec hook, which will be removed and should not run. + _initMockPluginPreExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a runtime validation function that will remove the mock plugin's pre exec + // hook. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: false, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.RUNTIME_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger the + // mock plugin's runtime validation function to remove the mock plugin's pre exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the + // mock plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.preExecutionHook.selector), + 0 // Should be called 0 times + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_RTValidation_replace_exec() public { + // Source: Runtime-Validation + // Target: Exec (different phase) + // Replace: replacement should run + + // Set up the mock plugin with an Exec function, which will replace the one defined by the ASM plugin + // and should be run. + _initMockPluginExecFunction(); + + // Install the ASM plugin with a runtime validation function that will replace the exec function. + // Runtime validation is also needed to allow the call to be performed. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: false, + setPreExec: false, + setPostExec: false + }); + // Encode two self-calls: one to uninstall ASM plugin, one to install the mock plugin. + Call[] memory calls = _generateCallsUninstallASMInstallMock(); + asmPlugin.setCallback( + abi.encodeCall(IStandardExecutor.executeBatch, (calls)), + AccountStateMutatingPlugin.FunctionId.RUNTIME_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger + // the ASM plugin's runtime validation function to replace the exec function with the mock plugin's exec + // function. The replacement should run, not the original. + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(AccountStateMutatingPlugin.executionFunction.selector), + 0 // Should be called 0 times + ); + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(AccountStateMutatingPlugin.executionFunction.selector), + 1 // Should be called 1 time + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_RTValidation_remove_exec() public { + // Source: Runtime-Validation + // Target: Exec (different phase) + // Removal: should revert as empty + + // Install the ASM plugin with a runtime validation function that will remove the exec function. + // Runtime validation is also needed to allow the call to be performed. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: false, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(asmPlugin), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.RUNTIME_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger + // the ASM plugin's runtime validation function to remove the exec function. This should cause the call to + // revert, but only after the ASM plugin's runtime validation function has run. + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.runtimeValidationFunction.selector), + 1 // Should be called 1 time + ); + vm.expectRevert( + abi.encodeWithSelector( + UpgradeableModularAccount.UnrecognizedFunction.selector, + AccountStateMutatingPlugin.executionFunction.selector + ) + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_RTValidation_add_postExec_associated_firstElement() public { + // Source: Runtime-Validation + // Target: post-Exec (different phase) + // Addition (associated, first pre-exec): should run + + // Set up the mock plugin with an associated post-Exec hook, which will be added and should run. + _initMockPluginPreAndPostExecutionHook(); + + // Install the ASM plugin with a runtime validation function that will add a post exec hook. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: false, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.RUNTIME_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger + // the ASM plugin's runtime validation function to install the mock plugin's post exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the + // mock plugin's hook should run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_RTValidation_add_postExec_associated_notFirstElement() public { + // Source: Runtime-Validation + // Target: post-Exec (different phase) + // Addition (associated, non-first pre-exec): should run + + // Set up the mock plugin with an associated post-Exec hook, which will be added and should run. + _initMockPluginPreAndPostExecutionHook(); + + // Install the ASM plugin with a runtime validation function that will add a post exec hook. + // It also needs a pre exec hook to ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: false, + setPreExec: true, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.RUNTIME_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger + // the ASM plugin's runtime validation function to install the mock plugin's post exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the + // mock plugin's hook should run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_RTValidation_remove_postExec_associated_firstElement() public { + // Source: Runtime-Validation + // Target: post-Exec (different phase) + // Removal (associated, first pre-exec): should *not* run + + // Set up the mock plugin with an associated post-Exec hook, which will be removed and should not run. + _initMockPluginPreAndPostExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a runtime validation function that will remove the mock plugin's post exec + // hook. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: false, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.RUNTIME_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger + // the ASM plugin's runtime validation function to remove the mock plugin's post exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the + // mock plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_RTValidation_remove_postExec_associated_notFirstElement() public { + // Source: Runtime-Validation + // Target: post-Exec (different phase) + // Removal (associated, non-first pre-exec): should *not* run + + // Set up the mock plugin with an associated post-Exec hook, which will be removed and should not run. + _initMockPluginPreAndPostExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a runtime validation function that will remove the mock plugin's post exec + // hook. It also needs a pre exec hook to ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: false, + setPreExec: true, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.RUNTIME_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger + // the ASM plugin's runtime validation function to remove the mock plugin's post exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the + // mock plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_RTValidation_add_postExec_firstElement() public { + // Source: Runtime-Validation + // Target: post-Exec (different phase) + // Addition (first post-only): should run + + // Set up the mock plugin with a post-Exec hook, which will be added and should run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the ASM plugin with a runtime validation function that will add a post exec hook. + // It also needs a runtime validation function to allow the call to be performed. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: false, + setPostExec: false, + setPreExec: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.RUNTIME_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger + // the ASM plugin's runtime validation function to install the mock plugin's post exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the + // mock plugin's hook should run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_RTValidation_add_postExec_notFirstElement() public { + // Source: Runtime-Validation + // Target: post-Exec (different phase) + // Addition (non-first post-only): should run + + // Set up the mock plugin with a post-Exec hook, which will be added and should run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the ASM plugin with a runtime validation function that will add a post exec hook. + // It also needs a post-only exec hook to ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: false, + setPostExec: true, + setPreExec: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.RUNTIME_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger + // the ASM plugin's runtime validation function to install the mock plugin's post exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the + // mock plugin's hook should run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_RTValidation_remove_postExec_firstElement() public { + // Source: Runtime-Validation + // Target: post-Exec (different phase) + // Removal (first post-only): should *not* run + + // Set up the mock plugin with a post-Exec hook, which will be removed and should not run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a runtime validation function that will remove the mock plugin's post exec + // hook. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: false, + setPostExec: false, + setPreExec: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.RUNTIME_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger + // the ASM plugin's runtime validation function to remove the mock plugin's post exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the + // mock plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } + + function test_ASP_RTValidation_remove_postExec_notFirstElement() public { + // Source: Runtime-Validation + // Target: post-Exec (different phase) + // Removal (non-first post-only): should *not* run + + // Set up the mock plugin with a post-Exec hook, which will be removed and should not run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a runtime validation function that will remove the mock plugin's post exec + // hook. It also needs a post-only exec hook to ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: false, + setPreUOValidation: false, + setRTValidation: true, + setPreRTValidation: false, + setPostExec: true, + setPreExec: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.RUNTIME_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a direct runtime call. This will trigger + // the ASM plugin's runtime validation function to remove the mock plugin's post exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the + // mock plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.prank(owner1); + AccountStateMutatingPlugin(address(account1)).executionFunction(); + } +} diff --git a/test/account/phases/AccountStatePhasesUOValidation.t.sol b/test/account/phases/AccountStatePhasesUOValidation.t.sol new file mode 100644 index 00000000..a4851b9c --- /dev/null +++ b/test/account/phases/AccountStatePhasesUOValidation.t.sol @@ -0,0 +1,1341 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.21; + +import {AccountStatePhasesTest} from "./AccountStatePhases.t.sol"; + +import {IPluginManager} from "../../../src/interfaces/IPluginManager.sol"; +import {IPlugin} from "../../../src/interfaces/IPlugin.sol"; +import {IStandardExecutor, Call} from "../../../src/interfaces/IStandardExecutor.sol"; + +import {AccountStateMutatingPlugin} from "../../mocks/plugins/AccountStateMutatingPlugin.sol"; + +// Tests the account state phase behavior when the source of the state modification +// happens during user op validation. +contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { + // Test cases covered here: + // These are listed in the order they are run in the test suite. + // The "source" indicates which in which phase the plugin will perform a modification, and the "target" + // indicates which phase will change as a result of the modification. + // + // - Source: pre-UserOp-Validation + // - Target: pre-UserOp-Validation (same phase) + // - Addition: adding a hook should not result in that hook running. + // - Removal: removing a hook should still have the hook run. + // - Target: UserOp-Validation (same phase) + // - Replace: original should run + // - Removal: original should run + // - Target: pre-Runtime-Validation (different phase) + // - n/a - can’t run in the same user op + // - Target: Runtime-Validation (different phase) + // - n/a - can’t run in the same user op + // - Target: pre-Exec (different phase) + // - Addition (first element): should run + // - Addition (not first): should run + // - Removal: should *not* run + // - Target: Exec (different phase) + // - Replace: replacement should run + // - Removal: should revert as empty + // - Target: post-Exec (different phase) + // - Addition (associated, first pre-exec): should run + // - Addition (associated, non-first pre-exec): should run + // - Removal (associated, first pre-exec): should *not* run + // - Removal (associated, non-first pre-exec): should *not* run + // - Addition (first post-only): should run + // - Addition (non-first post-only): should run + // - Removal (first post-only): should *not* run + // - Removal (non-first post-only): should *not* run + // - Source: UserOp-Validation + // - Target: pre-UserOp-Validation (same phase) + // - n/a - happens before user op validation + // - Target: UserOp-Validation (same phase) + // - Won’t test, since it’s the same single-element field. + // - Target: pre-Runtime-Validation (different phase) + // - n/a - can’t run in the same user op + // - Target: Runtime-Validation (different phase) + // - n/a - can’t run in the same user op + // - Target: pre-Exec (different phase) + // - Addition (first element): should run + // - Addition (not first): should run + // - Removal: should *not* run + // - Target: Exec (different phase) + // - Replace: replacement should run + // - Removal: should revert as empty + // - Target: post-Exec (different phase) + // - Addition (associated, first pre-exec): should run + // - Addition (associated, non-first pre-exec): should run + // - Removal (associated, first pre-exec): should *not* run + // - Removal (associated, non-first pre-exec): should *not* run + // - Addition (first post-only): should run + // - Addition (non-first post-only): should run + // - Removal (first post-only): should *not* run + // - Removal (non-first post-only): should *not* run + + function test_ASP_preUOValidation_add_preUOValidation() public { + // Source: pre-UserOp-Validation + // Target: pre-UserOp-Validation (same phase) + // Addition: adding a hook should not result in that hook running. + + // Set up the mock plugin with a pre-UserOp-Validation hook, which will be added and should not run. + _initMockPluginPreUserOpValidationHook(); + + // Install the ASM plugin with a pre user op validation hook that will add a pre user op validation hook. + // It also needs a user op validation function to allow the call to be performed. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: true, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin to + // install the mock plugin's pre user op validation hook during the first pre-UserOp-Validation hook. + // Per the 6900 spec, the state change should not be applied yet and the mock plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.preUserOpValidationHook.selector), + 0 // Should be called 0 times + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_preUOValidation_remove_preUOValidation() public { + // Source: pre-UserOp-Validation + // Target: pre-UserOp-Validation (same phase) + // Removal: removing a hook should still have the hook run. + + // Set up the mock plugin with a pre-UserOp-Validation hook, which will be removed and should still run. + _initMockPluginPreUserOpValidationHook(); + + // Install the plugin as part of the starting state. By installing this first, it will run AFTER the ASM + // plugin's pre-UserOp-Validation hook, giving the modification a chance to change the logic. + _installMockPlugin(); + + // Install the ASM plugin with a pre user op validation hook that will remove the mock plugin's pre user op + // validation hook. + // It also needs a user op validation function to allow the call to be performed. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: true, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK + ); + _installASMPlugin(); + + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.preUserOpValidationHook.selector), + 1 // Should be called 1 time + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_preUOValidation_replace_UOValidation() public { + // Source: pre-UserOp-Validation + // Target: UserOp-Validation (same phase) + // Replace: original should run + + // Set up the mock plugin with a userOpValidation function, which will replace the one defined by the ASM + // plugin and should not be run. + _initMockPluginUserOpValidationFunction(); + + // Install the ASM plugin with a pre user op validation hook that will replace its own user op validation + // function. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: true, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: false, + setPostExec: false + }); + // Encode two self-calls: one to uninstall ASM plugin, one to install the mock plugin. + Call[] memory calls = _generateCallsUninstallASMInstallMock(); + asmPlugin.setCallback( + abi.encodeCall(IStandardExecutor.executeBatch, (calls)), + AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin to + // replace its own user op validation function with the mock plugin's user op validation function during + // the + // first pre-UserOp-Validation hook. The original should run, not the replacement. + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.userOpValidationFunction.selector), + 1 // Should be called 1 time + ); + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.userOpValidationFunction.selector), + 0 // Should be called 0 times + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_preUOValidation_remove_UOValidation() public { + // Source: pre-UserOp-Validation + // Target: UserOp-Validation (same phase) + // Removal: original should run + + // Install the ASM plugin with a pre user op validation hook that will remove its own user op validation + // function. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: true, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: false, + setPostExec: false + }); + + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(asmPlugin), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin to + // remove its own user op validation function during the first pre-UserOp-Validation hook. The original + // should run. + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.userOpValidationFunction.selector), + 1 // Should be called 1 time + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + // Source: pre-UserOp-Validation + // Target: pre-Runtime-Validation (different phase) + // n/a - can’t run in the same user op + + // Source: pre-UserOp-Validation + // Target: Runtime-Validation (different phase) + // n/a - can’t run in the same user op + + function test_ASP_preUOValidation_add_preExec_firstElement() public { + // Source: pre-UserOp-Validation + // Target: pre-Exec (different phase) + // Addition (first element): should run + + // Set up the mock plugin with a pre-Exec hook, which will be added and should run. + _initMockPluginPreExecutionHook(); + + // Install the ASM plugin with a pre user op validation hook that will add a pre exec hook. + // It also needs a user op validation function to allow the call to be performed. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: true, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin to + // install the mock plugin's pre exec hook during the first pre-UserOp-Validation hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the mock + // plugin's hook should run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.preExecutionHook.selector), + 1 // Should be called 1 time + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_preUOValidation_add_preExec_notFirstElement() public { + // Source: pre-UserOp-Validation + // Target: pre-Exec (different phase) + // Addition (not first): should run + + // Set up the mock plugin with a pre-Exec hook, which will be added and should run. + _initMockPluginPreExecutionHook(); + + // Install the ASM plugin with a pre user op validation hook that will add a pre exec hook. + // It also needs a user op validation function to allow the call to be performed, and a pre exec hook to + // ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: true, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: true, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin to + // install the mock plugin's pre exec hook during the first pre-UserOp-Validation hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the mock + // plugin's hook should run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.preExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.preExecutionHook.selector), + 1 // Should be called 1 time + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_preUOValidation_remove_preExec() public { + // Source: pre-UserOp-Validation + // Target: pre-Exec (different phase) + // Removal: should *not* run + + // Set up the mock plugin with a pre-Exec hook, which will be removed and should not run. + _initMockPluginPreExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a pre user op validation hook that will remove the mock plugin's pre exec + // hook. + // It also needs a user op validation function to allow the call to be performed. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: true, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin to + // remove the mock plugin's pre exec hook during the first pre-UserOp-Validation hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the mock + // plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.preExecutionHook.selector), + 0 // Should be called 0 times + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_preUOValidation_replace_exec() public { + // Source: pre-UserOp-Validation + // Target: Exec (different phase) + // Replace: replacement should run + + // Set up the mock plugin with an exec function, which will replace the one defined by the ASM plugin and + // should be run. + _initMockPluginExecFunction(); + + // Install the ASM plugin with a pre user op validation hook that will replace the exec function. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: true, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: false, + setPostExec: false + }); + // Encode two self-calls: one to uninstall ASM plugin, one to install the mock plugin. + Call[] memory calls = _generateCallsUninstallASMInstallMock(); + asmPlugin.setCallback( + abi.encodeCall(IStandardExecutor.executeBatch, (calls)), + AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin to + // replace its own exec function with the mock plugin's exec function during the first + // pre-UserOp-Validation + // hook. The replacement should run, not the original. + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(AccountStateMutatingPlugin.executionFunction.selector), + 0 // Should be called 0 times + ); + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(AccountStateMutatingPlugin.executionFunction.selector), + 1 // Should be called 1 time + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_preUOValidation_remove_exec() public { + // Source: pre-UserOp-Validation + // Target: Exec (different phase) + // Removal: should revert as empty + + // Install the ASM plugin with a pre user op validation hook that will remove the exec function. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: true, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(asmPlugin), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin to + // remove its own exec function during the first pre-UserOp-Validation hook. Then, the call should revert + // during the execution phase because the exec function is empty. + + // Cannot use vm.expectRevert because it would only apply to the top-level call to `handleOps`, not the + // internal call. Instead, we use expectCall with a count of 0. + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(AccountStateMutatingPlugin.executionFunction.selector), + 0 // Should be called 0 times + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_preUOValidation_add_postExec_associated_firstElement() public { + // Source: pre-UserOp-Validation + // Target: post-Exec (different phase) + // Addition (associated, first pre-exec): should run + + // Set up the mock plugin with an associated post-Exec hook, which will be added and should run. + _initMockPluginPreAndPostExecutionHook(); + + // Install the ASM plugin with a pre user op validation hook that will add a post exec hook. + // It also needs a user op validation function to allow the call to be performed. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: true, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin to + // install the mock plugin's associated post exec hook during the first pre-UserOp-Validation hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the mock + // plugin's hook should run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_preUOValidation_add_postExec_associated_notFirstElement() public { + // Source: pre-UserOp-Validation + // Target: post-Exec (different phase) + // Addition (associated, non-first pre-exec): should run + + // Set up the mock plugin with an associated post-Exec hook, which will be added and should run. + _initMockPluginPreAndPostExecutionHook(); + + // Install the ASM plugin with a pre user op validation hook that will add a post exec hook. + // It also needs a user op validation function to allow the call to be performed, and a pre exec hook to + // ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: true, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: true, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin to + // install the mock plugin's associated post exec hook during the first pre-UserOp-Validation hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the mock + // plugin's hook should run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_preUOValidation_remove_postExec_associated_firstElement() public { + // Source: pre-UserOp-Validation + // Target: post-Exec (different phase) + // Removal (associated, first pre-exec): should *not* run + + // Set up the mock plugin with an associated post-Exec hook, which will be removed and should not run. + _initMockPluginPreAndPostExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a pre user op validation hook that will remove the mock plugin's associated + // post exec hook. + // It also needs a user op validation function to allow the call to be performed. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: true, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin to + // remove the mock plugin's associated post exec hook during the first pre-UserOp-Validation hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the mock + // plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_preUOValidation_remove_postExec_associated_notFirstElement() public { + // Source: pre-UserOp-Validation + // Target: post-Exec (different phase) + // Removal (associated, non-first pre-exec): should *not* run + + // Set up the mock plugin with an associated post-Exec hook, which will be removed and should not run. + _initMockPluginPreAndPostExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a pre user op validation hook that will remove the mock plugin's associated + // post exec hook. + // It also needs a user op validation function to allow the call to be performed, and a pre exec hook to + // ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: true, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: true, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin to + // remove the mock plugin's associated post exec hook during the first pre-UserOp-Validation hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the mock + // plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_preUOValidation_add_postExec_firstElement() public { + // Source: pre-UserOp-Validation + // Target: post-Exec (different phase) + // Addition (first post-only): should run + + // Set up the mock plugin with a post-Exec hook, which will be added and should run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the ASM plugin with a pre user op validation hook that will add a post exec hook. + // It also needs a user op validation function to allow the call to be performed. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: true, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin to + // install the mock plugin's post exec hook during the first pre-UserOp-Validation hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the mock + // plugin's hook should run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_preUOValidation_add_postExec_notFirstElement() public { + // Source: pre-UserOp-Validation + // Target: post-Exec (different phase) + // Addition (non-first post-only): should run + + // Set up the mock plugin with a post-Exec hook, which will be added and should run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the ASM plugin with a pre user op validation hook that will add a post exec hook. + // It also needs a user op validation function to allow the call to be performed, and a pre exec hook to + // ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: true, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: false, + setPostExec: true + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin to + // install the mock plugin's post exec hook during the first pre-UserOp-Validation hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the mock + // plugin's hook should run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_preUOValidation_remove_postExec_firstElement() public { + // Source: pre-UserOp-Validation + // Target: post-Exec (different phase) + // Removal (first post-only): should *not* run + + // Set up the mock plugin with a post-Exec hook, which will be removed and should not run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a pre user op validation hook that will remove the mock plugin's post exec + // hook. + // It also needs a user op validation function to allow the call to be performed. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: true, + setRTValidation: false, + setPreRTValidation: false, + setPostExec: false, + setPreExec: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin to + // remove the mock plugin's post exec hook during the first pre-UserOp-Validation hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the mock + // plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_preUOValidation_remove_postExec_notFirstElement() public { + // Source: pre-UserOp-Validation + // Target: post-Exec (different phase) + // Removal (non-first post-only): should *not* run + + // Set up the mock plugin with a post-Exec hook, which will be removed and should not run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a pre user op validation hook that will remove the mock plugin's post exec + // hook. + // It also needs a user op validation function to allow the call to be performed, and a post-only exec hook + // to ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: true, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: false, + setPostExec: true + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin to + // remove the mock plugin's post exec hook during the first pre-UserOp-Validation hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the mock + // plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + // Source: UserOp-Validation + // Target: pre-UserOp-Validation (same phase) + // n/a - happens before user op validation + + // Source: UserOp-Validation + // Target: UserOp-Validation (same phase) + // Won’t test, since it’s the same single-element field. + + // Source: UserOp-Validation + // Target: pre-Runtime-Validation (different phase) + // n/a - can’t run in the same user op + + // Source: UserOp-Validation + // Target: Runtime-Validation (different phase) + // n/a - can’t run in the same user op + + function test_ASP_UOValidation_add_preExec_firstElement() public { + // Source: UserOp-Validation + // Target: pre-Exec (different phase) + // Addition (first element): should run + + // Set up the mock plugin with a pre-Exec hook, which will be added and should run. + _initMockPluginPreExecutionHook(); + + // Install the ASM plugin with a user op validation function that will add a pre exec hook. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: false, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.USER_OP_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin's + // user op validation function to install the mock plugin's pre exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the mock + // plugin's hook should run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.preExecutionHook.selector), + 1 // Should be called 1 time + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_UOValidation_add_preExec_notFirstElement() public { + // Source: UserOp-Validation + // Target: pre-Exec (different phase) + // Addition (not first): should run + + // Set up the mock plugin with a pre-Exec hook, which will be added and should run. + _initMockPluginPreExecutionHook(); + + // Install the ASM plugin with a user op validation function that will add a pre exec hook. + // It also needs a pre exec hook to ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: false, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: true, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.USER_OP_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin's + // user op validation function to install the mock plugin's pre exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the mock + // plugin's hook should run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.preExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.preExecutionHook.selector), + 1 // Should be called 1 time + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_UOValidation_remove_preExec() public { + // Source: UserOp-Validation + // Target: pre-Exec (different phase) + // Removal: should *not* run + + // Set up the mock plugin with a pre-Exec hook, which will be removed and should not run. + _initMockPluginPreExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a user op validation function that will remove the mock plugin's pre exec + // hook. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: false, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.USER_OP_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin's + // user op validation function to remove the mock plugin's pre exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the mock + // plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.preExecutionHook.selector), + 0 // Should be called 0 times + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_UOValidation_replace_exec() public { + // Source: UserOp-Validation + // Target: Exec (different phase) + // Replace: replacement should run + + // Set up the mock plugin with an exec function, which will replace the one defined by the ASM plugin and + // should be run. + _initMockPluginExecFunction(); + + // Install the ASM plugin with a user op validation function that will replace the exec function. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: false, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: false, + setPostExec: false + }); + // Encode two self-calls: one to uninstall ASM plugin, one to install the mock plugin. + Call[] memory calls = _generateCallsUninstallASMInstallMock(); + asmPlugin.setCallback( + abi.encodeCall(IStandardExecutor.executeBatch, (calls)), + AccountStateMutatingPlugin.FunctionId.USER_OP_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin's + // user op validation function to replace the exec function with the mock plugin's exec function. The + // replacement should run, not the original. + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(AccountStateMutatingPlugin.executionFunction.selector), + 0 // Should be called 0 times + ); + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(AccountStateMutatingPlugin.executionFunction.selector), + 1 // Should be called 1 time + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_UOValidation_remove_exec() public { + // Source: UserOp-Validation + // Target: Exec (different phase) + // Removal: should revert as empty + + // Install the ASM plugin with a user op validation function that will remove the exec function. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: false, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(asmPlugin), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.USER_OP_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin's + // user op validation function to remove the exec function. Then, the call should revert during the + // execution phase because the exec function is empty. + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(AccountStateMutatingPlugin.executionFunction.selector), + 0 // Should be called 0 times + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_UOValidation_add_postExec_associated_firstElement() public { + // Source: UserOp-Validation + // Target: post-Exec (different phase) + // Addition (associated, first pre-exec): should run + + // Set up the mock plugin with an associated post-Exec hook, which will be added and should run. + _initMockPluginPreAndPostExecutionHook(); + + // Install the ASM plugin with a user op validation function that will add a post exec hook. + // It also needs a user op validation function to allow the call to be performed. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: false, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.USER_OP_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin's + // user op validation function to install the mock plugin's associated post exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the mock + // plugin's hook should run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_UOValidation_add_postExec_associated_notFirstElement() public { + // Source: UserOp-Validation + // Target: post-Exec (different phase) + // Addition (associated, non-first pre-exec): should run + + // Set up the mock plugin with an associated post-Exec hook, which will be added and should run. + _initMockPluginPreAndPostExecutionHook(); + + // Install the ASM plugin with a user op validation function that will add a post exec hook. + // It also needs a user op validation function to allow the call to be performed, and a pre exec hook to + // ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: false, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: true, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.USER_OP_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin's + // user op validation function to install the mock plugin's associated post exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the mock + // plugin's hook should run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_UOValidation_remove_postExec_associated_firstElement() public { + // Source: UserOp-Validation + // Target: post-Exec (different phase) + // Removal (associated, first pre-exec): should *not* run + + // Set up the mock plugin with an associated post-Exec hook, which will be removed and should not run. + _initMockPluginPreAndPostExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a user op validation function that will remove the mock plugin's associated + // post exec hook. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: false, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: false, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.USER_OP_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin's + // user op validation function to remove the mock plugin's associated post exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the mock + // plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_UOValidation_remove_postExec_associated_notFirstElement() public { + // Source: UserOp-Validation + // Target: post-Exec (different phase) + // Removal (associated, non-first pre-exec): should *not* run + + // Set up the mock plugin with an associated post-Exec hook, which will be removed and should not run. + _initMockPluginPreAndPostExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a user op validation function that will remove the mock plugin's associated + // post exec hook. + // It also needs a user op validation function to allow the call to be performed, and a pre exec hook to + // ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: false, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: true, + setPostExec: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.USER_OP_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin's + // user op validation function to remove the mock plugin's associated post exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the mock + // plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_UOValidation_add_postExec_firstElement() public { + // Source: UserOp-Validation + // Target: post-Exec (different phase) + // Addition (first post-only): should run + + // Set up the mock plugin with a post-Exec hook, which will be added and should run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the ASM plugin with a user op validation function that will add a post exec hook. + // It also needs a user op validation function to allow the call to be performed. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: false, + setRTValidation: false, + setPreRTValidation: false, + setPostExec: false, + setPreExec: false + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.USER_OP_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin's + // user op validation function to install the mock plugin's post exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the mock + // plugin's hook should run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_UOValidation_add_postExec_notFirstElement() public { + // Source: UserOp-Validation + // Target: post-Exec (different phase) + // Addition (non-first post-only): should run + + // Set up the mock plugin with a post-Exec hook, which will be added and should run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the ASM plugin with a user op validation function that will add a post exec hook. + // It also needs a user op validation function to allow the call to be performed, and a post-only exec hook + // to + // ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: false, + setRTValidation: false, + setPreRTValidation: false, + setPreExec: false, + setPostExec: true + }); + asmPlugin.setCallback( + abi.encodeCall( + IPluginManager.installPlugin, + (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + ), + AccountStateMutatingPlugin.FunctionId.USER_OP_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin's + // user op validation function to install the mock plugin's post exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the mock + // plugin's hook should run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_UOValidation_remove_postExec_firstElement() public { + // Source: UserOp-Validation + // Target: post-Exec (different phase) + // Removal (first post-only): should *not* run + + // Set up the mock plugin with a post-Exec hook, which will be removed and should not run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a user op validation function that will remove the mock plugin's post exec + // hook. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: false, + setRTValidation: false, + setPreRTValidation: false, + setPostExec: false, + setPreExec: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.USER_OP_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin's + // user op validation function to remove the mock plugin's post exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the mock + // plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } + + function test_ASP_UOValidation_remove_postExec_notFirstElement() public { + // Source: UserOp-Validation + // Target: post-Exec (different phase) + // Removal (non-first post-only): should *not* run + + // Set up the mock plugin with a post-Exec hook, which will be removed and should not run. + _initMockPluginPostOnlyExecutionHook(); + + // Install the mock plugin as part of the starting state. + _installMockPlugin(); + + // Install the ASM plugin with a user op validation function that will remove the mock plugin's post exec + // hook. + // It also needs a user op validation function to allow the call to be performed, and a post-only exec hook + // to ensure that the mock plugin's hook is not the first one. + asmPlugin.configureInstall({ + setUOValidation: true, + setPreUOValidation: false, + setRTValidation: false, + setPreRTValidation: false, + setPostExec: true, + setPreExec: false + }); + asmPlugin.setCallback( + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + AccountStateMutatingPlugin.FunctionId.USER_OP_VALIDATION + ); + _installASMPlugin(); + + // Call the `executionFunction` function on the account via a user op. This will trigger the ASM plugin's + // user op validation function to remove the mock plugin's post exec hook. + // Per the 6900 spec, because this is in a different phase, the state change should be applied and the mock + // plugin's hook should not run. + vm.expectCall( + address(mockPlugin1), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 0 // Should be called 0 times + ); + vm.expectCall( + address(asmPlugin), + // Partial calldata is provided to match against different parameters. + abi.encodeWithSelector(IPlugin.postExecutionHook.selector), + 1 // Should be called 1 time + ); + entryPoint.handleOps(_generateAndSignUserOp(), beneficiary); + } +} diff --git a/test/mocks/plugins/AccountStateMutatingPlugin.sol b/test/mocks/plugins/AccountStateMutatingPlugin.sol new file mode 100644 index 00000000..cad12603 --- /dev/null +++ b/test/mocks/plugins/AccountStateMutatingPlugin.sol @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.21; + +import { + PluginManifest, + ManifestExecutionHook, + ManifestFunction, + ManifestAssociatedFunctionType, + ManifestAssociatedFunction +} from "../../../src/interfaces/IPlugin.sol"; +import {UserOperation} from "../../../src/interfaces/erc4337/UserOperation.sol"; + +import {BaseTestPlugin} from "./BaseTestPlugin.sol"; + +// Used in conjunction with AccountStatePhasesTest to verify that the account state is consistent when plugins are +// updated mid-execution. +contract AccountStateMutatingPlugin is BaseTestPlugin { + enum FunctionId { + PRE_USER_OP_VALIDATION_HOOK, + USER_OP_VALIDATION, + PRE_RUNTIME_VALIDATION_HOOK, + RUNTIME_VALIDATION, + PRE_EXECUTION_HOOK, + EXECUTION_FUNCTION, // Not actually used as a function id in the manifest, just makes it easier to write + // the callback setter + POST_EXECUTION_HOOK + } + + bool hasUOValidation; + bool hasPreUOValidation; + bool hasRTValidation; + bool hasPreRTValidation; + bool hasPreExec; + bool hasPostExec; + + bytes UOValidationCallback; + bytes preUOValidationCallback; + bytes RTValidationCallback; + bytes preRTValidationCallback; + bytes preExecCallback; + bytes execCallback; + bytes postExecCallback; + + // Specify what functions should be added when this is installed. + function configureInstall( + bool setUOValidation, + bool setPreUOValidation, + bool setRTValidation, + bool setPreRTValidation, + bool setPreExec, + bool setPostExec + ) public { + hasUOValidation = setUOValidation; + hasPreUOValidation = setPreUOValidation; + hasRTValidation = setRTValidation; + hasPreRTValidation = setPreRTValidation; + hasPreExec = setPreExec; + hasPostExec = setPostExec; + } + + function setCallback(bytes calldata callback, FunctionId where) external { + if (where == FunctionId.PRE_USER_OP_VALIDATION_HOOK) { + preUOValidationCallback = callback; + } else if (where == FunctionId.USER_OP_VALIDATION) { + UOValidationCallback = callback; + } else if (where == FunctionId.PRE_RUNTIME_VALIDATION_HOOK) { + preRTValidationCallback = callback; + } else if (where == FunctionId.RUNTIME_VALIDATION) { + RTValidationCallback = callback; + } else if (where == FunctionId.PRE_EXECUTION_HOOK) { + preExecCallback = callback; + } else if (where == FunctionId.EXECUTION_FUNCTION) { + execCallback = callback; + } else if (where == FunctionId.POST_EXECUTION_HOOK) { + postExecCallback = callback; + } else { + revert NotImplemented(); + } + } + + function pluginManifest() external pure override returns (PluginManifest memory) { + return _castToPure(_getManifest)(); + } + + function _castToPure(function() internal view returns (PluginManifest memory) fnIn) + internal + pure + returns (function() internal pure returns (PluginManifest memory) fnOut) + { + assembly { + fnOut := fnIn + } + } + + function _getManifest() internal view returns (PluginManifest memory) { + PluginManifest memory m; + + // Always add the execution function + m.executionFunctions = new bytes4[](1); + m.executionFunctions[0] = this.executionFunction.selector; + + // Conditionally add the other functions + + if (hasPreUOValidation) { + m.preUserOpValidationHooks = new ManifestAssociatedFunction[](1); + m.preUserOpValidationHooks[0] = ManifestAssociatedFunction({ + executionSelector: this.executionFunction.selector, + associatedFunction: ManifestFunction({ + functionType: ManifestAssociatedFunctionType.SELF, + functionId: uint8(FunctionId.PRE_USER_OP_VALIDATION_HOOK), + dependencyIndex: 0 // Unused + }) + }); + } + + if (hasUOValidation) { + m.userOpValidationFunctions = new ManifestAssociatedFunction[](1); + m.userOpValidationFunctions[0] = ManifestAssociatedFunction({ + executionSelector: this.executionFunction.selector, + associatedFunction: ManifestFunction({ + functionType: ManifestAssociatedFunctionType.SELF, + functionId: uint8(FunctionId.USER_OP_VALIDATION), + dependencyIndex: 0 // Unused + }) + }); + } + + if (hasPreRTValidation) { + m.preRuntimeValidationHooks = new ManifestAssociatedFunction[](1); + m.preRuntimeValidationHooks[0] = ManifestAssociatedFunction({ + executionSelector: this.executionFunction.selector, + associatedFunction: ManifestFunction({ + functionType: ManifestAssociatedFunctionType.SELF, + functionId: uint8(FunctionId.PRE_RUNTIME_VALIDATION_HOOK), + dependencyIndex: 0 // Unused + }) + }); + } + + if (hasRTValidation) { + m.runtimeValidationFunctions = new ManifestAssociatedFunction[](1); + m.runtimeValidationFunctions[0] = ManifestAssociatedFunction({ + executionSelector: this.executionFunction.selector, + associatedFunction: ManifestFunction({ + functionType: ManifestAssociatedFunctionType.SELF, + functionId: uint8(FunctionId.RUNTIME_VALIDATION), + dependencyIndex: 0 // Unused + }) + }); + } + + if (hasPreExec && hasPostExec) { + m.executionHooks = new ManifestExecutionHook[](1); + m.executionHooks[0] = ManifestExecutionHook({ + executionSelector: this.executionFunction.selector, + preExecHook: ManifestFunction({ + functionType: ManifestAssociatedFunctionType.SELF, + functionId: uint8(FunctionId.PRE_EXECUTION_HOOK), + dependencyIndex: 0 // Unused + }), + postExecHook: ManifestFunction({ + functionType: ManifestAssociatedFunctionType.SELF, + functionId: uint8(FunctionId.POST_EXECUTION_HOOK), + dependencyIndex: 0 // Unused + }) + }); + } else if (hasPreExec) { + m.executionHooks = new ManifestExecutionHook[](1); + m.executionHooks[0] = ManifestExecutionHook({ + executionSelector: this.executionFunction.selector, + preExecHook: ManifestFunction({ + functionType: ManifestAssociatedFunctionType.SELF, + functionId: uint8(FunctionId.PRE_EXECUTION_HOOK), + dependencyIndex: 0 // Unused + }), + postExecHook: ManifestFunction({ + functionType: ManifestAssociatedFunctionType.NONE, + functionId: 0, // Unused + dependencyIndex: 0 // Unused + }) + }); + } else if (hasPostExec) { + m.executionHooks = new ManifestExecutionHook[](1); + m.executionHooks[0] = ManifestExecutionHook({ + executionSelector: this.executionFunction.selector, + preExecHook: ManifestFunction({ + functionType: ManifestAssociatedFunctionType.NONE, + functionId: 0, // Unused + dependencyIndex: 0 // Unused + }), + postExecHook: ManifestFunction({ + functionType: ManifestAssociatedFunctionType.SELF, + functionId: uint8(FunctionId.POST_EXECUTION_HOOK), + dependencyIndex: 0 // Unused + }) + }); + } + + return m; + } + + // Empty implementations of install/uninstall + + function onInstall(bytes calldata) external override {} + + function onUninstall(bytes calldata) external override {} + + // Plugin functions + + function preUserOpValidationHook(uint8 functionId, UserOperation calldata, bytes32) + external + override + returns (uint256) + { + if (functionId == uint8(FunctionId.PRE_USER_OP_VALIDATION_HOOK)) { + _performCallbackIfNonempty(preUOValidationCallback); + return 0; + } + revert NotImplemented(); + } + + function userOpValidationFunction(uint8 functionId, UserOperation calldata, bytes32) + external + override + returns (uint256) + { + if (functionId == uint8(FunctionId.USER_OP_VALIDATION)) { + _performCallbackIfNonempty(UOValidationCallback); + return 0; + } + revert NotImplemented(); + } + + function preRuntimeValidationHook(uint8 functionId, address, uint256, bytes calldata) external override { + if (functionId == uint8(FunctionId.PRE_RUNTIME_VALIDATION_HOOK)) { + _performCallbackIfNonempty(preRTValidationCallback); + return; + } + revert NotImplemented(); + } + + function runtimeValidationFunction(uint8 functionId, address, uint256, bytes calldata) external override { + if (functionId == uint8(FunctionId.RUNTIME_VALIDATION)) { + _performCallbackIfNonempty(RTValidationCallback); + return; + } + revert NotImplemented(); + } + + function preExecutionHook(uint8 functionId, address, uint256, bytes calldata) + external + override + returns (bytes memory) + { + if (functionId == uint8(FunctionId.PRE_EXECUTION_HOOK)) { + _performCallbackIfNonempty(preExecCallback); + return ""; + } + revert NotImplemented(); + } + + function executionFunction() external { + _performCallbackIfNonempty(execCallback); + } + + function postExecutionHook(uint8 functionId, bytes calldata) external override { + if (functionId == uint8(FunctionId.POST_EXECUTION_HOOK)) { + _performCallbackIfNonempty(postExecCallback); + return; + } + revert NotImplemented(); + } + + function _performCallbackIfNonempty(bytes storage callback) internal { + if (callback.length > 0) { + (bool success,) = msg.sender.call(callback); + require(success, "Callback failed"); + } + } +} diff --git a/test/mocks/plugins/ComprehensivePlugin.sol b/test/mocks/plugins/ComprehensivePlugin.sol index 16029de7..31e58419 100644 --- a/test/mocks/plugins/ComprehensivePlugin.sol +++ b/test/mocks/plugins/ComprehensivePlugin.sol @@ -117,6 +117,9 @@ contract ComprehensivePlugin is BaseTestPlugin { function pluginManifest() external pure override returns (PluginManifest memory) { PluginManifest memory manifest; + manifest.permittedExecutionSelectors = new bytes4[](1); + manifest.permittedExecutionSelectors[0] = this.foo.selector; + manifest.executionFunctions = new bytes4[](1); manifest.executionFunctions[0] = this.foo.selector; diff --git a/test/plugin/TokenReceiverPlugin.t.sol b/test/plugin/TokenReceiverPlugin.t.sol index 35b1f8b8..1ab6543d 100644 --- a/test/plugin/TokenReceiverPlugin.t.sol +++ b/test/plugin/TokenReceiverPlugin.t.sol @@ -93,7 +93,8 @@ contract TokenReceiverPluginTest is Test, IERC1155Receiver, AccountStorageV1 { function test_failERC721Transfer() public { vm.expectRevert( abi.encodeWithSelector( - UpgradeableModularAccount.UnrecognizedFunction.selector, IERC721Receiver.onERC721Received.selector + UpgradeableModularAccount.RuntimeValidationFunctionMissing.selector, + IERC721Receiver.onERC721Received.selector ) ); t0.safeTransferFrom(address(this), address(acct), _TOKEN_ID); @@ -109,7 +110,8 @@ contract TokenReceiverPluginTest is Test, IERC1155Receiver, AccountStorageV1 { function test_failERC777Transfer() public { vm.expectRevert( abi.encodeWithSelector( - UpgradeableModularAccount.UnrecognizedFunction.selector, IERC777Recipient.tokensReceived.selector + UpgradeableModularAccount.RuntimeValidationFunctionMissing.selector, + IERC777Recipient.tokensReceived.selector ) ); t1.transfer(address(acct), _TOKEN_AMOUNT); From 3b73246ab67ba462206f403f6f6f6daf48d0eeb2 Mon Sep 17 00:00:00 2001 From: howy <132113803+howydev@users.noreply.github.com> Date: Thu, 18 Jan 2024 12:02:47 -0500 Subject: [PATCH 056/106] fix: [spearbit-58] improve natSpec (#72) --- src/factory/MultiOwnerMSCAFactory.sol | 4 +++- src/factory/MultiOwnerTokenReceiverMSCAFactory.sol | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/factory/MultiOwnerMSCAFactory.sol b/src/factory/MultiOwnerMSCAFactory.sol index b05329d7..116de48d 100644 --- a/src/factory/MultiOwnerMSCAFactory.sol +++ b/src/factory/MultiOwnerMSCAFactory.sol @@ -47,7 +47,7 @@ contract MultiOwnerMSCAFactory is Ownable2Step { /// @notice Create a modular smart contract account /// @dev Account address depends on salt, impl addr, plugins and plugin init data /// @dev The owner array must be in strictly ascending order and not include the 0 address. - /// @param salt salt for additional entropy for create2 + /// @param salt salt for create2 /// @param owners address array of the owners function createAccount(uint256 salt, address[] calldata owners) external returns (address addr) { if (!FactoryHelpers.isValidOwnerArray(owners)) { @@ -116,7 +116,9 @@ contract MultiOwnerMSCAFactory is Ownable2Step { /// @notice Getter for counterfactual address based on input params /// @dev The owner array must be in strictly ascending order and not include the 0 address. + /// @param salt salt for additional entropy for create2 /// @param owners array of addresses of the owner + /// @return address of counterfactual account function getAddress(uint256 salt, address[] calldata owners) external view returns (address) { // array can't be empty if (owners.length == 0) { diff --git a/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol b/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol index b590a9ad..9313f4c1 100644 --- a/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol +++ b/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol @@ -126,6 +126,7 @@ contract MultiOwnerTokenReceiverMSCAFactory is Ownable2Step { /// @dev The owner array must be in strictly ascending order and not include the 0 address. /// @param salt salt for additional entropy for create2 /// @param owners array of addresses of the owner + /// @return address of counterfactual account function getAddress(uint256 salt, address[] calldata owners) external view returns (address) { // array can't be empty if (owners.length == 0) { From 73d5051ff1aa437994a2fa6bee9775babb906e92 Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Thu, 18 Jan 2024 14:43:34 -0800 Subject: [PATCH 057/106] fix: [spearbit-101] Spend limit interval comments (#75) --- .../permissions/ISessionKeyPermissionsUpdates.sol | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol b/src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol index 3354d3f7..8ca6bf84 100644 --- a/src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol +++ b/src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol @@ -35,20 +35,28 @@ interface ISessionKeyPermissionsUpdates { /// @notice Sets the native token spend limit for a session key. This specifies how much of the native token /// the session key may use, optionally with a refresh interval that specifies how often the limit is reset. + /// If a refresh interval is already set and a new refresh interval is specified, then any existing interval + /// spend data will be cleared and a new interval will start. /// @param spendLimit The maximum amount of native token the session key may spend. /// @param refreshInterval The time interval over which the spend limit is enforced. If zero, there is no time /// interval by which the limit is refreshed. function setNativeTokenSpendLimit(uint256 spendLimit, uint48 refreshInterval) external; - /// @notice Sets the ERC-20 spend limit for a session key. + /// @notice Sets the ERC-20 spend limit for a session key. This specifies how much of the ERC-20 token the + /// session key may use, optionally with a refresh interval that specifies how often the limit is reset. If + /// a refresh interval is already set and a new refresh interval is specified, then any existing interval + /// spend data will be cleared and a new interval will start. /// @param token The ERC-20 token address. /// @param spendLimit The maximum amount of the ERC-20 token the session key may spend. /// @param refreshInterval The time interval over which the spend limit is enforced. If zero, the spend limit /// is never refreshed. function setERC20SpendLimit(address token, uint256 spendLimit, uint48 refreshInterval) external; - /// @notice Sets the gas spend limit for a session key. Note that the session key permissions enforcement will - /// usually overestimate the gas usage per user operation. + /// @notice Sets the gas spend limit for a session key. This specifies how much of the native token the + /// session key may spend on gas fees, optionally with a refresh interval that specifies how often the limit + /// is reset. If a refresh interval is already set and a new refresh interval is specified, then any existing + /// interval spend data will be cleared and a new interval will start. Note that the session key permissions + /// enforcement will usually overestimate the gas usage per user operation. /// @dev If the account is staked, a malicious session key user may abuse gas limits to cause reputation damage /// to the account. This is because when a gas limit is set, there are state updates during validation that can /// potentially cause future user ops in the same bundle to fail. Intelligent bundlers may re-simulate and From c53f36a8d94b315d7f05f0c41eed245f9172682a Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Thu, 18 Jan 2024 14:44:42 -0800 Subject: [PATCH 058/106] fix: [spearbit-105] Remove extraneous comment (#74) --- src/account/AccountExecutor.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/src/account/AccountExecutor.sol b/src/account/AccountExecutor.sol index e76c5a28..fce39689 100644 --- a/src/account/AccountExecutor.sol +++ b/src/account/AccountExecutor.sol @@ -210,7 +210,6 @@ abstract contract AccountExecutor { // Add the abi-encoded fields length (128 bytes) and the selector's size (4 bytes) // to the error size. errorSize := add(errorSize, 132) - // errorSize := add(errorSize, 132) // Store the selector in the start of the error buffer. // Any set lower bits will be cleared with the subsequest mstore. mstore(errorStart, errorSelector) From e7bca9fa1169f9f7015468978e2c6ea734f7e1db Mon Sep 17 00:00:00 2001 From: howy <132113803+howydev@users.noreply.github.com> Date: Fri, 19 Jan 2024 12:15:09 -0500 Subject: [PATCH 059/106] fix: [spearbit-90] add warnings for _coalescePreValidation helper function (#78) --- src/helpers/ValidationDataHelpers.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helpers/ValidationDataHelpers.sol b/src/helpers/ValidationDataHelpers.sol index 92c83baf..69c0238d 100644 --- a/src/helpers/ValidationDataHelpers.sol +++ b/src/helpers/ValidationDataHelpers.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.21; +/// @dev This helper function assumes that uint160(validationData1) and uint160(validationData2) can only be 0 or 1 // solhint-disable-next-line private-vars-leading-underscore function _coalescePreValidation(uint256 validationData1, uint256 validationData2) pure From 617677a0703da3107e5980c89162eb510254ae16 Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Fri, 19 Jan 2024 13:38:04 -0800 Subject: [PATCH 060/106] fix: [spearbit-79,102][quantstamp-13] Merge session key plugin with permissions plugin (#62) --- script/Deploy.s.sol | 11 - src/plugins/session/ISessionKeyPlugin.sol | 193 ++++++++++-- src/plugins/session/SessionKeyPlugin.sol | 297 +++++++++++------- .../ISessionKeyPermissionsPlugin.sol | 159 ---------- .../ISessionKeyPermissionsUpdates.sol | 5 +- ...nsPlugin.sol => SessionKeyPermissions.sol} | 266 +++------------- .../permissions/SessionKeyPermissionsBase.sol | 22 +- .../SessionKeyPermissionsLoupe.sol | 24 +- .../SessionKeyPluginWithMultiOwner.t.sol | 257 +++++++++------ .../SessionKeyERC20SpendLimits.t.sol | 211 +++++-------- .../permissions/SessionKeyGasLimits.t.sol | 113 +++---- .../SessionKeyNativeTokenSpendLimits.t.sol | 119 +++---- ...ugin.t.sol => SessionKeyPermissions.t.sol} | 258 +++++++-------- 13 files changed, 865 insertions(+), 1070 deletions(-) delete mode 100644 src/plugins/session/permissions/ISessionKeyPermissionsPlugin.sol rename src/plugins/session/permissions/{SessionKeyPermissionsPlugin.sol => SessionKeyPermissions.sol} (73%) rename test/plugin/session/permissions/{SessionKeyPermissionsPlugin.t.sol => SessionKeyPermissions.t.sol} (69%) diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index 9413b1ae..e80a797a 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -17,7 +17,6 @@ import {BasePlugin} from "../src/plugins/BasePlugin.sol"; import {MultiOwnerPlugin} from "../src/plugins/owner/MultiOwnerPlugin.sol"; import {TokenReceiverPlugin} from "../src/plugins/TokenReceiverPlugin.sol"; import {SessionKeyPlugin} from "../src/plugins/session/SessionKeyPlugin.sol"; -import {SessionKeyPermissionsPlugin} from "../src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol"; contract Deploy is Script { // Load entrypoint from env @@ -40,7 +39,6 @@ contract Deploy is Script { address public tokenReceiverPlugin = vm.envOr("TOKEN_RECEIVER_PLUGIN", address(0)); bytes32 public tokenReceiverPluginManifestHash; address public sessionKeyPlugin = vm.envOr("SESSION_KEY_PLUGIN", address(0)); - address public sessionKeyPermissionsPlugin = vm.envOr("SESSION_KEY_PERMS_PLUGIN", address(0)); function run() public { console.log("******** Deploying *********"); @@ -123,15 +121,6 @@ contract Deploy is Script { console.log("Exist SessionKeyPlugin: ", sessionKeyPlugin); } - // Deploy SessionKeyPermissionsPlugin impl - if (sessionKeyPermissionsPlugin == address(0)) { - SessionKeyPermissionsPlugin skpp = new SessionKeyPermissionsPlugin(); - sessionKeyPermissionsPlugin = address(skpp); - console.log("New SessionKeyPermissionsPlugin: ", sessionKeyPermissionsPlugin); - } else { - console.log("Exist SessionKeyPermissionsPlugin: ", sessionKeyPermissionsPlugin); - } - console.log("******** Deploy Done! *********"); vm.stopBroadcast(); } diff --git a/src/plugins/session/ISessionKeyPlugin.sol b/src/plugins/session/ISessionKeyPlugin.sol index 5bf38808..362bf209 100644 --- a/src/plugins/session/ISessionKeyPlugin.sol +++ b/src/plugins/session/ISessionKeyPlugin.sol @@ -9,16 +9,60 @@ interface ISessionKeyPlugin { USER_OP_VALIDATION_SESSION_KEY } + // Valid access control types for contract access control lists. + enum ContractAccessControlType { + ALLOWLIST, // Allowlist is default + DENYLIST, + NONE + } + + // Struct returned by view functions to provide information about a session key's spend limit. + // Used for native token, ERC-20, and gas spend limits. + struct SpendLimitInfo { + bool hasLimit; + uint256 limit; + uint256 limitUsed; + uint48 refreshInterval; + uint48 lastUsedTime; + } + + /// @notice Emitted when a session key is added. + /// @param account The account that owns the session key. + /// @param sessionKey The session key that was added. + /// @param tag The tag that was associated with the key. + event SessionKeyAdded(address indexed account, address indexed sessionKey, bytes32 indexed tag); + + /// @notice Emitted when a session key is removed. + /// @param account The account that owns the session key. + /// @param sessionKey The session key that was removed. + event SessionKeyRemoved(address indexed account, address indexed sessionKey); + + /// @notice Emitted when a session key is rotated, which replaces a key while keeping the same permissions. + /// @dev Rotating a key into itself is possible, and does not change the key's permissions. + /// @param account The account that owns the session key. + /// @param oldSessionKey The session key that was rotated away. + /// @param newSessionKey The session key that was rotated to. + event SessionKeyRotated(address indexed account, address indexed oldSessionKey, address indexed newSessionKey); + + /// @notice Emitted when a session key's permissions are updated. + /// @param account The account that owns the session key. + /// @param sessionKey The session key that was updated. + /// @param updates The updates that were performed. Updates are ABI-encoded calls to the functions defined in + /// `ISessionKeyPermissionsUpdates`, and are not external functions implemented by this contract. + event PermissionsUpdated(address indexed account, address indexed sessionKey, bytes[] updates); + error InvalidSessionKey(address sessionKey); - error NotAuthorized(address caller); error SessionKeyNotFound(address sessionKey); error UnableToRemove(address sessionKey); error InvalidSignature(address sessionKey); + error ERC20SpendLimitExceeded(address account, address sessionKey, address token); + error InvalidPermissionsUpdate(); + error InvalidToken(); + error NativeTokenSpendLimitExceeded(address account, address sessionKey); - struct SessionKeyToRemove { - address sessionKey; - bytes32 predecessor; - } + // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + // ┃ Execution functions ┃ + // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ /// @notice Perform a batch execution with a session key. /// @dev The session key address is included as a parameter so context may be preserved across validation and @@ -28,26 +72,44 @@ interface ISessionKeyPlugin { /// @return The array of return data from the executions. function executeWithSessionKey(Call[] calldata calls, address sessionKey) external returns (bytes[] memory); - /// @notice Get the session keys of the account. - /// @return The array of session keys of the account. - function getSessionKeys() external view returns (address[] memory); + /// @notice Add a session key. + /// @param sessionKey The session key to register. + /// @param tag An optional tag that can be used to identify the key. + function addSessionKey(address sessionKey, bytes32 tag) external; - /// @notice Check if a session key is a session key of the account. - /// @param sessionKey The session key to check. - /// @return The boolean whether the session key is a session key of the account. - function isSessionKey(address sessionKey) external view returns (bool); - - /// @notice Add and remove session keys from the account. - /// Note that the session keys to remove will be removed prior to any being added, and they will be removed in - /// order from first to last. If the predecessor changes due to a prior removal, the caller should pass in the - /// updated predecessor. - /// @param sessionKeysToAdd The array of session keys to add to the account. - /// @param sessionKeysToRemove The array of session keys to remove from the account, along with their - /// predecessor in the list. - function updateSessionKeys( - address[] calldata sessionKeysToAdd, - SessionKeyToRemove[] calldata sessionKeysToRemove - ) external; + /// @notice Remove a session key. + /// @param sessionKey The session key to remove. + /// @param predecessor The list predecessor of the key, as returned by `findPredecessor`. + function removeSessionKey(address sessionKey, bytes32 predecessor) external; + + /// @notice Move a session key's registration status and existing permissions to another session key. + /// @param oldSessionKey The session key to move. + /// @param predecessor The list predecessor of the old session key, as returned by `findPredecessor`. + /// @param newSessionKey The session key to move to. + function rotateSessionKey(address oldSessionKey, bytes32 predecessor, address newSessionKey) external; + + /// @notice Performs a sequence of updates to a session key's permissions. These updates are abi-encoded calls + /// to the functions defined in `ISessionKeyPermissionsUpdates`, and are not external functions implemented by + /// this contract. + /// @param sessionKey The session key for which to update permissions. + /// @param updates The abi-encoded updates to perform. + function updateKeyPermissions(address sessionKey, bytes[] calldata updates) external; + + // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + // ┃ Plugin-only function ┃ + // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + + /// @notice An externally available function, callable by anyone, that resets the "last used" timestamp on a + /// session key. This helps a session key get "unstuck" if it was used in a setting where every call it made + /// while using a new interval's gas limit reverted. Since this plugin internally tracks when that reset should + /// happen, this function does not need other validation. + /// @param account The account that owns the session key. + /// @param sessionKey The session key to reset. + function resetSessionKeyGasLimitTimestamp(address account, address sessionKey) external; + + // ┏━━━━━━━━━━━━━━━━━━━━━━┓ + // ┃ View functions ┃ + // ┗━━━━━━━━━━━━━━━━━━━━━━┛ /// @notice Get the session keys of the account. /// This function is not added to accounts during installation. @@ -69,4 +131,87 @@ interface ISessionKeyPlugin { /// @param sessionKey The session key to check. /// @return The list predecessor of the session key. function findPredecessor(address account, address sessionKey) external view returns (bytes32); + + /// @notice Get the access control type for a session key on an account. + /// @param account The account to check. + /// @param sessionKey The session key to check. + /// @return The access control type for the session key on the account. + function getAccessControlType(address account, address sessionKey) + external + view + returns (ContractAccessControlType); + + /// @notice Get an access control entry for a session key on an account. + /// @param account The account to check. + /// @param sessionKey The session key to check. + /// @param targetAddress The target address to check. + /// @return isOnList Whether the target address is on the list (either allowlist or blocklist depending on the + /// access control type). + /// @return checkSelectors Whether the target address should be checked for selectors during permissions + /// enforcement. + function getAccessControlEntry(address account, address sessionKey, address targetAddress) + external + view + returns (bool isOnList, bool checkSelectors); + + /// @notice Get whether a selector is on the access control list for a session key on an account. + /// @param account The account to check. + /// @param sessionKey The session key to check. + /// @param targetAddress The target address to check. + /// @param selector The selector to check. + /// @return isOnList Whether the selector is on the list (either allowlist or blocklist depending on the + /// access control type). + function isSelectorOnAccessControlList( + address account, + address sessionKey, + address targetAddress, + bytes4 selector + ) external view returns (bool isOnList); + + /// @notice Get the active time range for a session key on an account. + /// @param account The account to check. + /// @param sessionKey The session key to check. + /// @return validAfter The time after which the session key is valid. + /// @return validUntil The time until which the session key is valid. + function getKeyTimeRange(address account, address sessionKey) + external + view + returns (uint48 validAfter, uint48 validUntil); + + /// @notice Get the native token spend limit for a session key on an account. + /// @param account The account to check. + /// @param sessionKey The session key to check. + /// @return A struct with fields describing the state of native token spending limits on this session key. + function getNativeTokenSpendLimitInfo(address account, address sessionKey) + external + view + returns (SpendLimitInfo memory); + + /// @notice Get the gas spend limit for a session key on an account. + /// @param account The account to check. + /// @param sessionKey The session key to check. + /// @return info A struct with fields describing the state of gas spending limits on this session key. + /// @return shouldReset Whether this session key must be reset by calling `resetSessionKeyGasLimitTimestamp` + /// before it can be used. + function getGasSpendLimit(address account, address sessionKey) + external + view + returns (SpendLimitInfo memory info, bool shouldReset); + + /// @notice Get the ERC20 spend limit for a session key on an account. + /// @param account The account to check. + /// @param sessionKey The session key to check. + /// @param token The token to check. + /// @return A struct with fields describing the state of ERC20 spending limits on this session key. + function getERC20SpendLimitInfo(address account, address sessionKey, address token) + external + view + returns (SpendLimitInfo memory); + + /// @notice Get the required paymaster address for a session key on an account, if any. + /// @param account The account to check. + /// @param sessionKey The session key to check. + /// @return The required paymaster address for this session key on this account, or the zero address if the + /// rule is disabled. + function getRequiredPaymaster(address account, address sessionKey) external view returns (address); } diff --git a/src/plugins/session/SessionKeyPlugin.sol b/src/plugins/session/SessionKeyPlugin.sol index 23c26167..17135b40 100644 --- a/src/plugins/session/SessionKeyPlugin.sol +++ b/src/plugins/session/SessionKeyPlugin.sol @@ -5,6 +5,7 @@ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {BasePlugin} from "../BasePlugin.sol"; import {ISessionKeyPlugin} from "./ISessionKeyPlugin.sol"; +import {SessionKeyPermissions} from "./permissions/SessionKeyPermissions.sol"; import {IPlugin} from "../../interfaces/IPlugin.sol"; import {IPluginExecutor} from "../../interfaces/IPluginExecutor.sol"; @@ -28,9 +29,12 @@ import {SetValue, SENTINEL_VALUE} from "../../libraries/LinkedListSetUtils.sol"; /// @title Session Key Plugin /// @author Alchemy /// @notice This plugin allows users to set session keys that can be used to validate user operations performing -/// external calls. It does not enforce any permissions on what the keys can do, that must be configured via other -/// plugins with hooks. -contract SessionKeyPlugin is BasePlugin, ISessionKeyPlugin { +/// external calls. It also implements customizable permissions for session keys, supporting: +/// - Allowlist/denylist on addresses and function selectors. +/// - Time range for when a session key may be used. +/// - Spend limits on native token and ERC-20 tokens. +/// - Gas spend limits, either from the account's balance or from a specified paymaster. +contract SessionKeyPlugin is ISessionKeyPlugin, SessionKeyPermissions, BasePlugin { using ECDSA for bytes32; using AssociatedLinkedListSetLib for AssociatedLinkedListSet; @@ -45,14 +49,70 @@ contract SessionKeyPlugin is BasePlugin, ISessionKeyPlugin { uint256 internal constant _MANIFEST_DEPENDENCY_INDEX_OWNER_USER_OP_VALIDATION = 0; uint256 internal constant _MANIFEST_DEPENDENCY_INDEX_OWNER_RUNTIME_VALIDATION = 1; + // Storage fields + AssociatedLinkedListSet internal _sessionKeys; + // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + // ┃ Plugin interface functions ┃ + // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + + /// @inheritdoc BasePlugin + function userOpValidationFunction(uint8 functionId, UserOperation calldata userOp, bytes32 userOpHash) + external + override + returns (uint256) + { + if (functionId == uint8(FunctionId.USER_OP_VALIDATION_SESSION_KEY)) { + (Call[] memory calls, address sessionKey) = abi.decode(userOp.callData[4:], (Call[], address)); + bytes32 hash = userOpHash.toEthSignedMessageHash(); + + (address recoveredSig, ECDSA.RecoverError err) = hash.tryRecover(userOp.signature); + if (err == ECDSA.RecoverError.NoError) { + if ( + _sessionKeys.contains(msg.sender, CastLib.toSetValue(sessionKey)) && sessionKey == recoveredSig + ) { + return _checkUserOpPermissions(userOp, calls, sessionKey); + } + return _SIG_VALIDATION_FAILED; + } + revert InvalidSignature(sessionKey); + } + revert NotImplemented(); + } + + /// @inheritdoc BasePlugin + function onUninstall(bytes calldata) external override { + // Unset the key id for all session keys. + address[] memory sessionKeys = CastLib.toAddressArray(_sessionKeys.getAll(msg.sender)); + uint256 length = sessionKeys.length; + for (uint256 i = 0; i < length;) { + _updateSessionKeyId(msg.sender, sessionKeys[i], SessionKeyId.wrap(bytes32(0))); + + emit SessionKeyRemoved(msg.sender, sessionKeys[i]); + + unchecked { + ++i; + } + } + + _sessionKeys.clear(msg.sender); + // Note that we do not reset the key id counter `_keyIdCounter` for the account, in order to prevent + // permissions configured from a previous installation from being re-used. + } + // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ // ┃ Execution functions ┃ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ /// @inheritdoc ISessionKeyPlugin - function executeWithSessionKey(Call[] calldata calls, address) external returns (bytes[] memory) { + function executeWithSessionKey(Call[] calldata calls, address sessionKey) + external + override + returns (bytes[] memory) + { + _updateLimitsPreExec(msg.sender, calls, sessionKey); + uint256 callsLength = calls.length; bytes[] memory results = new bytes[](callsLength); @@ -70,70 +130,86 @@ contract SessionKeyPlugin is BasePlugin, ISessionKeyPlugin { } /// @inheritdoc ISessionKeyPlugin - function getSessionKeys() external view returns (address[] memory) { - SetValue[] memory values = _sessionKeys.getAll(msg.sender); + function addSessionKey(address sessionKey, bytes32 tag) public override { + if (!_sessionKeys.tryAdd(msg.sender, CastLib.toSetValue(sessionKey))) { + // This check ensures no duplicate keys and that the session key is not the zero address. + revert InvalidSessionKey(sessionKey); + } - return CastLib.toAddressArray(values); + // Register the key with a new ID, and update the ID counter + // We use pre increment to prevent the first id from being zero. + // We don't need to check whether or not the session key id exists, because we know it to be atomic with + // add/remove operations on the _sessionKeys storage variable, and therefore must not be registered. + _updateSessionKeyId(msg.sender, sessionKey, SessionKeyId.wrap(bytes32(++_keyIdCounter[msg.sender]))); + + emit SessionKeyAdded(msg.sender, sessionKey, tag); } /// @inheritdoc ISessionKeyPlugin - function isSessionKey(address sessionKey) external view returns (bool) { - return _sessionKeys.contains(msg.sender, CastLib.toSetValue(sessionKey)); + function removeSessionKey(address sessionKey, bytes32 predecessor) external override { + if (!_sessionKeys.tryRemoveKnown(msg.sender, CastLib.toSetValue(sessionKey), predecessor)) { + revert InvalidSessionKey(sessionKey); + } + + // Unset the key id for the session key. + // We don't need to check that the key exists, because we know it to be atomic with add/remove operations + // on the _sessionKeys storage variable, and therefore must be registered. + _updateSessionKeyId(msg.sender, sessionKey, SessionKeyId.wrap(bytes32(0))); + + emit SessionKeyRemoved(msg.sender, sessionKey); } /// @inheritdoc ISessionKeyPlugin - function updateSessionKeys( - address[] calldata sessionKeysToAdd, - SessionKeyToRemove[] calldata sessionKeysToRemove - ) external { - uint256 length = sessionKeysToRemove.length; - for (uint256 i = 0; i < length;) { - if ( - !_sessionKeys.tryRemoveKnown( - msg.sender, - CastLib.toSetValue(sessionKeysToRemove[i].sessionKey), - sessionKeysToRemove[i].predecessor - ) - ) { - revert UnableToRemove(sessionKeysToRemove[i].sessionKey); - } + function rotateSessionKey(address oldSessionKey, bytes32 predecessor, address newSessionKey) + external + override + { + if (!_sessionKeys.tryRemoveKnown(msg.sender, CastLib.toSetValue(oldSessionKey), predecessor)) { + revert InvalidSessionKey(oldSessionKey); + } - unchecked { - ++i; - } + // If the new key to rotate into is a duplicate or the zero address, revert. + if (!_sessionKeys.tryAdd(msg.sender, CastLib.toSetValue(newSessionKey))) { + revert InvalidSessionKey(newSessionKey); } - length = sessionKeysToAdd.length; - for (uint256 i = 0; i < length;) { - // This also checks that sessionKeysToAdd[i] is not zero. - if (!_sessionKeys.tryAdd(msg.sender, CastLib.toSetValue(sessionKeysToAdd[i]))) { - revert InvalidSessionKey(sessionKeysToAdd[i]); - } + SessionKeyId oldSessionKeyId = _sessionKeyIdOf(msg.sender, oldSessionKey); + _updateSessionKeyId(msg.sender, oldSessionKey, SessionKeyId.wrap(bytes32(0))); + _updateSessionKeyId(msg.sender, newSessionKey, oldSessionKeyId); - unchecked { - ++i; - } - } + emit SessionKeyRotated(msg.sender, oldSessionKey, newSessionKey); } + // The function `updateKeyPermissions` is implemented in `SessionKeyPermissions`. + + // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + // ┃ Plugin-only function ┃ + // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + + // The function `resetSessionKeyGasLimitTimestamp` is implemented in `SessionKeyPermissions`. + // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ // ┃ Plugin view functions ┃ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ /// @inheritdoc ISessionKeyPlugin - function sessionKeysOf(address account) external view returns (address[] memory) { + function sessionKeysOf(address account) external view override returns (address[] memory) { SetValue[] memory values = _sessionKeys.getAll(account); return CastLib.toAddressArray(values); } /// @inheritdoc ISessionKeyPlugin - function isSessionKeyOf(address account, address sessionKey) external view returns (bool) { + function isSessionKeyOf(address account, address sessionKey) external view override returns (bool) { return _sessionKeys.contains(account, CastLib.toSetValue(sessionKey)); } + // ┏━━━━━━━━━━━━━━━━━━━━━━┓ + // ┃ View functions ┃ + // ┗━━━━━━━━━━━━━━━━━━━━━━┛ + /// @inheritdoc ISessionKeyPlugin - function findPredecessor(address account, address sessionKey) external view returns (bytes32) { + function findPredecessor(address account, address sessionKey) external view override returns (bytes32) { address[] memory sessionKeys = CastLib.toAddressArray(_sessionKeys.getAll(account)); uint256 length = sessionKeys.length; @@ -153,59 +229,6 @@ contract SessionKeyPlugin is BasePlugin, ISessionKeyPlugin { revert SessionKeyNotFound(sessionKey); } - // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ - // ┃ Plugin interface functions ┃ - // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - - /// @inheritdoc BasePlugin - function userOpValidationFunction(uint8 functionId, UserOperation calldata userOp, bytes32 userOpHash) - external - view - override - returns (uint256) - { - if (functionId == uint8(FunctionId.USER_OP_VALIDATION_SESSION_KEY)) { - (, address sessionKey) = abi.decode(userOp.callData[4:], (Call[], address)); - bytes32 hash = userOpHash.toEthSignedMessageHash(); - - (address recoveredSig, ECDSA.RecoverError err) = hash.tryRecover(userOp.signature); - if (err == ECDSA.RecoverError.NoError) { - if ( - _sessionKeys.contains(msg.sender, CastLib.toSetValue(sessionKey)) && sessionKey == recoveredSig - ) { - return _SIG_VALIDATION_PASSED; - } else { - return _SIG_VALIDATION_FAILED; - } - } else { - revert InvalidSignature(sessionKey); - } - } - revert NotImplemented(); - } - - /// @inheritdoc BasePlugin - function _onInstall(bytes calldata data) internal override isNotInitialized(msg.sender) { - address[] memory sessionKeysToAdd = abi.decode(data, (address[])); - - uint256 length = sessionKeysToAdd.length; - for (uint256 i = 0; i < length;) { - // This also checks that sessionKeysToAdd[i] is not zero. - if (!_sessionKeys.tryAdd(msg.sender, CastLib.toSetValue(sessionKeysToAdd[i]))) { - revert InvalidSessionKey(sessionKeysToAdd[i]); - } - - unchecked { - ++i; - } - } - } - - /// @inheritdoc BasePlugin - function onUninstall(bytes calldata) external override { - _sessionKeys.clear(msg.sender); - } - /// @inheritdoc BasePlugin function pluginManifest() external pure override returns (PluginManifest memory) { PluginManifest memory manifest; @@ -216,11 +239,12 @@ contract SessionKeyPlugin is BasePlugin, ISessionKeyPlugin { manifest.dependencyInterfaceIds[_MANIFEST_DEPENDENCY_INDEX_OWNER_RUNTIME_VALIDATION] = type(IPlugin).interfaceId; - manifest.executionFunctions = new bytes4[](4); + manifest.executionFunctions = new bytes4[](5); manifest.executionFunctions[0] = this.executeWithSessionKey.selector; - manifest.executionFunctions[1] = this.getSessionKeys.selector; - manifest.executionFunctions[2] = this.isSessionKey.selector; - manifest.executionFunctions[3] = this.updateSessionKeys.selector; + manifest.executionFunctions[1] = this.addSessionKey.selector; + manifest.executionFunctions[2] = this.removeSessionKey.selector; + manifest.executionFunctions[3] = this.rotateSessionKey.selector; + manifest.executionFunctions[4] = this.updateKeyPermissions.selector; ManifestFunction memory sessionKeyUserOpValidationFunction = ManifestFunction({ functionType: ManifestAssociatedFunctionType.SELF, @@ -233,41 +257,51 @@ contract SessionKeyPlugin is BasePlugin, ISessionKeyPlugin { dependencyIndex: _MANIFEST_DEPENDENCY_INDEX_OWNER_USER_OP_VALIDATION }); - manifest.userOpValidationFunctions = new ManifestAssociatedFunction[](2); + manifest.userOpValidationFunctions = new ManifestAssociatedFunction[](5); manifest.userOpValidationFunctions[0] = ManifestAssociatedFunction({ executionSelector: this.executeWithSessionKey.selector, associatedFunction: sessionKeyUserOpValidationFunction }); manifest.userOpValidationFunctions[1] = ManifestAssociatedFunction({ - executionSelector: this.updateSessionKeys.selector, + executionSelector: this.addSessionKey.selector, + associatedFunction: ownerUserOpValidationFunction + }); + manifest.userOpValidationFunctions[2] = ManifestAssociatedFunction({ + executionSelector: this.removeSessionKey.selector, + associatedFunction: ownerUserOpValidationFunction + }); + manifest.userOpValidationFunctions[3] = ManifestAssociatedFunction({ + executionSelector: this.rotateSessionKey.selector, + associatedFunction: ownerUserOpValidationFunction + }); + manifest.userOpValidationFunctions[4] = ManifestAssociatedFunction({ + executionSelector: this.updateKeyPermissions.selector, associatedFunction: ownerUserOpValidationFunction }); // Session keys are only expected to be used for user op validation, so no runtime validation functions are // set over executeWithSessionKey, and pre runtime hook will always deny. - ManifestFunction memory alwaysAllowValidationFunction = ManifestFunction({ - functionType: ManifestAssociatedFunctionType.RUNTIME_VALIDATION_ALWAYS_ALLOW, - functionId: 0, // Unused. - dependencyIndex: 0 // Unused. - }); - ManifestFunction memory ownerRuntimeValidationFunction = ManifestFunction({ functionType: ManifestAssociatedFunctionType.DEPENDENCY, functionId: 0, // unused since it's a dependency dependencyIndex: _MANIFEST_DEPENDENCY_INDEX_OWNER_RUNTIME_VALIDATION }); - manifest.runtimeValidationFunctions = new ManifestAssociatedFunction[](3); + manifest.runtimeValidationFunctions = new ManifestAssociatedFunction[](4); manifest.runtimeValidationFunctions[0] = ManifestAssociatedFunction({ - executionSelector: this.getSessionKeys.selector, - associatedFunction: alwaysAllowValidationFunction + executionSelector: this.addSessionKey.selector, + associatedFunction: ownerRuntimeValidationFunction }); manifest.runtimeValidationFunctions[1] = ManifestAssociatedFunction({ - executionSelector: this.isSessionKey.selector, - associatedFunction: alwaysAllowValidationFunction + executionSelector: this.removeSessionKey.selector, + associatedFunction: ownerRuntimeValidationFunction }); manifest.runtimeValidationFunctions[2] = ManifestAssociatedFunction({ - executionSelector: this.updateSessionKeys.selector, + executionSelector: this.rotateSessionKey.selector, + associatedFunction: ownerRuntimeValidationFunction + }); + manifest.runtimeValidationFunctions[3] = ManifestAssociatedFunction({ + executionSelector: this.updateKeyPermissions.selector, associatedFunction: ownerRuntimeValidationFunction }); @@ -295,13 +329,26 @@ contract SessionKeyPlugin is BasePlugin, ISessionKeyPlugin { metadata.author = _AUTHOR; // Permission strings - string memory modifyOwnershipPermission = "Modify Session Keys"; + string memory modifySessionKeys = "Modify Session Keys"; + string memory modifySessionKeyPermissions = "Modify Session Key Permissions"; // Permission descriptions - metadata.permissionDescriptors = new SelectorPermission[](1); + metadata.permissionDescriptors = new SelectorPermission[](4); metadata.permissionDescriptors[0] = SelectorPermission({ - functionSelector: this.updateSessionKeys.selector, - permissionDescription: modifyOwnershipPermission + functionSelector: this.addSessionKey.selector, + permissionDescription: modifySessionKeys + }); + metadata.permissionDescriptors[1] = SelectorPermission({ + functionSelector: this.removeSessionKey.selector, + permissionDescription: modifySessionKeys + }); + metadata.permissionDescriptors[2] = SelectorPermission({ + functionSelector: this.rotateSessionKey.selector, + permissionDescription: modifySessionKeys + }); + metadata.permissionDescriptors[3] = SelectorPermission({ + functionSelector: this.updateKeyPermissions.selector, + permissionDescription: modifySessionKeyPermissions }); return metadata; @@ -316,6 +363,26 @@ contract SessionKeyPlugin is BasePlugin, ISessionKeyPlugin { return interfaceId == type(ISessionKeyPlugin).interfaceId || super.supportsInterface(interfaceId); } + // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + // ┃ Internal Functions ┃ + // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + + /// @inheritdoc BasePlugin + function _onInstall(bytes calldata data) internal override isNotInitialized(msg.sender) { + address[] memory sessionKeysToAdd = abi.decode(data, (address[])); + + uint256 length = sessionKeysToAdd.length; + for (uint256 i = 0; i < length;) { + // Use the public function to add the session key, set the key id, and emit the event. + // Note that no tags are set when adding keys with this method. + addSessionKey(sessionKeysToAdd[i], bytes32(0)); + + unchecked { + ++i; + } + } + } + /// @inheritdoc BasePlugin function _isInitialized(address account) internal view override returns (bool) { return !_sessionKeys.isEmpty(account); diff --git a/src/plugins/session/permissions/ISessionKeyPermissionsPlugin.sol b/src/plugins/session/permissions/ISessionKeyPermissionsPlugin.sol deleted file mode 100644 index 551f9eaa..00000000 --- a/src/plugins/session/permissions/ISessionKeyPermissionsPlugin.sol +++ /dev/null @@ -1,159 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; - -import {UserOperation} from "../../../interfaces/erc4337/UserOperation.sol"; - -interface ISessionKeyPermissionsPlugin { - enum FunctionId { - PRE_USER_OP_VALIDATION_HOOK_CHECK_PERMISSIONS, - PRE_EXECUTION_HOOK_UPDATE_LIMITS - } - - enum ContractAccessControlType { - ALLOWLIST, // Allowlist is default - DENYLIST, - NONE - } - - struct SpendLimitInfo { - bool hasLimit; - uint256 limit; - uint256 limitUsed; - uint48 refreshInterval; - uint48 lastUsedTime; - } - - /// @notice Emitted when a session key is registered. - /// @param account The account that owns the session key. - /// @param sessionKey The session key that was registered. - /// @param tag The tag that was associated with the key. - event KeyRegistered(address indexed account, address indexed sessionKey, bytes32 indexed tag); - - /// @notice Emitted when a session key's permissions are updated. - /// @param account The account that owns the session key. - /// @param sessionKey The session key that was updated. - /// @param updates The updates that were performed. Updates are ABI-encoded - event PermissionsUpdated(address indexed account, address indexed sessionKey, bytes[] updates); - - /// @notice Emitted when a session key is rotated, which transfers permissions from one key to another. - /// @param account The account that owns the session key. - /// @param oldSessionKey The session key that was rotated away. - /// @param newSessionKey The session key that was rotated to. - event KeyRotated(address indexed account, address indexed oldSessionKey, address indexed newSessionKey); - - error ERC20SpendLimitExceeded(address account, address sessionKey, address token); - error KeyAlreadyRegistered(address sessionKey); - error KeyNotRegistered(address sessionKey); - error InvalidPermissionsUpdate(); - error InvalidToken(); - error NativeTokenSpendLimitExceeded(address account, address sessionKey); - - /// @notice Register a key with the permissions plugin. Without this step, key cannot be used while the - /// permissions plugin is installed. - /// @param sessionKey The session key to register. - /// @param tag An optional tag that can be used to identify the key. - function registerKey(address sessionKey, bytes32 tag) external; - - /// @notice Move a session key's registration status and existing permissions to another session key. - /// @param oldSessionKey The session key to move. - /// @param newSessionKey The session key to move to. - function rotateKey(address oldSessionKey, address newSessionKey) external; - - /// @notice Performs a sequence of updates to a session key's permissions. These updates are abi-encoded calls - /// to the functions defined in `ISessionKeyPermissionsUpdates`, and are not external functions implemented by - /// this contract. - /// @param sessionKey The session key for which to update permissions. - /// @param updates The abi-encoded updates to perform. - function updateKeyPermissions(address sessionKey, bytes[] calldata updates) external; - - /// @notice An externally available function, callable by anyone, that resets the "last used" timestamp on a - /// session key. This helps a session key get "unstuck" if it was used in a setting where every call it made - /// while using a new interval's gas limit reverted. Since this plugin internally tracks when that reset should - /// happen, this function does not need other validation. - /// @param account The account that owns the session key. - /// @param sessionKey The session key to reset. - function resetSessionKeyGasLimitTimestamp(address account, address sessionKey) external; - - /// @notice Get the access control type for a session key on an account. - /// @param account The account to check. - /// @param sessionKey The session key to check. - /// @return The access control type for the session key on the account. - function getAccessControlType(address account, address sessionKey) - external - view - returns (ContractAccessControlType); - - /// @notice Get an access control entry for a session key on an account. - /// @param account The account to check. - /// @param sessionKey The session key to check. - /// @param targetAddress The target address to check. - /// @return isOnList Whether the target address is on the list (either allowlist or blocklist depending on the - /// access control type). - /// @return checkSelectors Whether the target address should be checked for selectors during permissions - /// enforcement. - function getAccessControlEntry(address account, address sessionKey, address targetAddress) - external - view - returns (bool isOnList, bool checkSelectors); - - /// @notice Get whether a selector is on the access control list for a session key on an account. - /// @param account The account to check. - /// @param sessionKey The session key to check. - /// @param targetAddress The target address to check. - /// @param selector The selector to check. - /// @return isOnList Whether the selector is on the list (either allowlist or blocklist depending on the - /// access control type). - function isSelectorOnAccessControlList( - address account, - address sessionKey, - address targetAddress, - bytes4 selector - ) external view returns (bool isOnList); - - /// @notice Get the active time range for a session key on an account. - /// @param account The account to check. - /// @param sessionKey The session key to check. - /// @return validAfter The time after which the session key is valid. - /// @return validUntil The time until which the session key is valid. - function getKeyTimeRange(address account, address sessionKey) - external - view - returns (uint48 validAfter, uint48 validUntil); - - /// @notice Get the native token spend limit for a session key on an account. - /// @param account The account to check. - /// @param sessionKey The session key to check. - /// @return A struct with fields describing the state of native token spending limits on this session key. - function getNativeTokenSpendLimitInfo(address account, address sessionKey) - external - view - returns (SpendLimitInfo memory); - - /// @notice Get the gas spend limit for a session key on an account. - /// @param account The account to check. - /// @param sessionKey The session key to check. - /// @return info A struct with fields describing the state of gas spending limits on this session key. - /// @return shouldReset Whether this session key must be reset by calling `resetSessionKeyGasLimitTimestamp` - /// before it can be used. - function getGasSpendLimit(address account, address sessionKey) - external - view - returns (SpendLimitInfo memory info, bool shouldReset); - - /// @notice Get the ERC20 spend limit for a session key on an account. - /// @param account The account to check. - /// @param sessionKey The session key to check. - /// @param token The token to check. - /// @return A struct with fields describing the state of ERC20 spending limits on this session key. - function getERC20SpendLimitInfo(address account, address sessionKey, address token) - external - view - returns (SpendLimitInfo memory); - - /// @notice Get the required paymaster address for a session key on an account, if any. - /// @param account The account to check. - /// @param sessionKey The session key to check. - /// @return The required paymaster address for this session key on this account, or the zero address if the - /// rule is disabled. - function getRequiredPaymaster(address account, address sessionKey) external view returns (address); -} diff --git a/src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol b/src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol index 8ca6bf84..9120644e 100644 --- a/src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol +++ b/src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.21; -import {ISessionKeyPermissionsPlugin} from "./ISessionKeyPermissionsPlugin.sol"; +import {ISessionKeyPlugin} from "../ISessionKeyPlugin.sol"; /// @notice This interface defines the functions that may be used to update a session key's permissions. /// The functions defined here are not actually implemented, but instead are abi-encoded as arguments to the @@ -12,8 +12,7 @@ interface ISessionKeyPermissionsUpdates { /// previous elements from the list are not cleared, and instead reinterpretted as entries in the new list /// type. /// @param contractAccessControlType The new access control type. - function setAccessListType(ISessionKeyPermissionsPlugin.ContractAccessControlType contractAccessControlType) - external; + function setAccessListType(ISessionKeyPlugin.ContractAccessControlType contractAccessControlType) external; /// @notice Add or remove a contract address from the access list, optionally specifying whether to check /// selectors. diff --git a/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol b/src/plugins/session/permissions/SessionKeyPermissions.sol similarity index 73% rename from src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol rename to src/plugins/session/permissions/SessionKeyPermissions.sol index b20474f7..4b8202ce 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol +++ b/src/plugins/session/permissions/SessionKeyPermissions.sol @@ -4,73 +4,23 @@ pragma solidity ^0.8.21; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {Call} from "../../../interfaces/IStandardExecutor.sol"; -import {BasePlugin} from "../../BasePlugin.sol"; -import {ISessionKeyPermissionsPlugin} from "./ISessionKeyPermissionsPlugin.sol"; -import {ISessionKeyPermissionsUpdates} from "./ISessionKeyPermissionsUpdates.sol"; import {ISessionKeyPlugin} from "../ISessionKeyPlugin.sol"; +import {ISessionKeyPermissionsUpdates} from "./ISessionKeyPermissionsUpdates.sol"; import {SessionKeyPermissionsLoupe} from "./SessionKeyPermissionsLoupe.sol"; -import { - IPlugin, - ManifestAssociatedFunction, - ManifestAssociatedFunctionType, - ManifestExecutionHook, - ManifestFunction, - PluginManifest, - PluginMetadata, - SelectorPermission -} from "../../../interfaces/IPlugin.sol"; import {IStandardExecutor} from "../../../interfaces/IStandardExecutor.sol"; import {UserOperation} from "../../../interfaces/erc4337/UserOperation.sol"; -/// @title Session Key Permissions Plugin +/// @title Session Key Permissions /// @author Alchemy /// @notice This plugin allows users to configure and enforce permissions on session keys that have been /// added by SessionKeyPlugin. -contract SessionKeyPermissionsPlugin is ISessionKeyPermissionsPlugin, SessionKeyPermissionsLoupe, BasePlugin { - string internal constant _NAME = "Session Key Permissions Plugin"; - string internal constant _VERSION = "1.0.0"; - string internal constant _AUTHOR = "Alchemy"; - - // Constants used in the manifest - uint256 internal constant _MANIFEST_DEPENDENCY_INDEX_OWNER_USER_OP_VALIDATION = 0; - uint256 internal constant _MANIFEST_DEPENDENCY_INDEX_OWNER_RUNTIME_VALIDATION = 1; - +abstract contract SessionKeyPermissions is ISessionKeyPlugin, SessionKeyPermissionsLoupe { // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ // ┃ Execution functions ┃ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - /// @inheritdoc ISessionKeyPermissionsPlugin - function registerKey(address sessionKey, bytes32 tag) external override { - SessionKeyId keyId = _sessionKeyIdOf(msg.sender, sessionKey); - if (SessionKeyId.unwrap(keyId) != 0) { - revert KeyAlreadyRegistered(sessionKey); - } - // Register the key with a new ID, and update the ID counter - // We use pre increment to prevent the first id from being zero - _updateSessionKeyId(msg.sender, sessionKey, SessionKeyId.wrap(bytes32(++_keyIdCounter[msg.sender]))); - - emit KeyRegistered(msg.sender, sessionKey, tag); - } - - /// @inheritdoc ISessionKeyPermissionsPlugin - function rotateKey(address oldSessionKey, address newSessionKey) external override { - // Assert that the new key is not already registered - SessionKeyId newKeyId = _sessionKeyIdOf(msg.sender, newSessionKey); - if (SessionKeyId.unwrap(newKeyId) != bytes32(0)) { - revert KeyAlreadyRegistered(newSessionKey); - } - // Assert that the old key is registered - SessionKeyId keyId = _sessionKeyIdOf(msg.sender, oldSessionKey); - _assertRegistered(keyId, oldSessionKey); - // Update the key ID of the old key to zero, and the new key to the old key's ID - _updateSessionKeyId(msg.sender, oldSessionKey, SessionKeyId.wrap(bytes32(0))); - _updateSessionKeyId(msg.sender, newSessionKey, keyId); - - emit KeyRotated(msg.sender, oldSessionKey, newSessionKey); - } - - /// @inheritdoc ISessionKeyPermissionsPlugin + /// @inheritdoc ISessionKeyPlugin function updateKeyPermissions(address sessionKey, bytes[] calldata updates) external override { (SessionKeyData storage sessionKeyData, SessionKeyId keyId) = _loadSessionKey(msg.sender, sessionKey); @@ -86,7 +36,7 @@ contract SessionKeyPermissionsPlugin is ISessionKeyPermissionsPlugin, SessionKey emit PermissionsUpdated(msg.sender, sessionKey, updates); } - /// @inheritdoc ISessionKeyPermissionsPlugin + /// @inheritdoc ISessionKeyPlugin function resetSessionKeyGasLimitTimestamp(address account, address sessionKey) external override { (SessionKeyData storage sessionKeyData,) = _loadSessionKey(account, sessionKey); if (sessionKeyData.gasLimitResetThisBundle) { @@ -95,170 +45,25 @@ contract SessionKeyPermissionsPlugin is ISessionKeyPermissionsPlugin, SessionKey } } - // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ - // ┃ Plugin interface functions ┃ - // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - - /// @inheritdoc BasePlugin - function preUserOpValidationHook(uint8 functionId, UserOperation calldata userOp, bytes32) - external - override - returns (uint256) - { - if (functionId == uint8(FunctionId.PRE_USER_OP_VALIDATION_HOOK_CHECK_PERMISSIONS)) { - return _checkUserOpPermissions(userOp); - } - revert NotImplemented(); - } - - function preExecutionHook(uint8 functionId, address, uint256, bytes calldata data) - external - override - returns (bytes memory) - { - if (functionId == uint8(FunctionId.PRE_EXECUTION_HOOK_UPDATE_LIMITS)) { - _updateLimitsPreExec(msg.sender, data); - } else { - revert NotImplemented(); - } - } - - /// @inheritdoc BasePlugin - function onInstall(bytes calldata) external override {} - - /// @inheritdoc BasePlugin - function onUninstall(bytes calldata) external override {} - - /// @inheritdoc BasePlugin - function pluginManifest() external pure override returns (PluginManifest memory) { - PluginManifest memory manifest; - - manifest.dependencyInterfaceIds = new bytes4[](2); - manifest.dependencyInterfaceIds[_MANIFEST_DEPENDENCY_INDEX_OWNER_USER_OP_VALIDATION] = - type(IPlugin).interfaceId; - manifest.dependencyInterfaceIds[_MANIFEST_DEPENDENCY_INDEX_OWNER_RUNTIME_VALIDATION] = - type(IPlugin).interfaceId; - - manifest.executionFunctions = new bytes4[](3); - manifest.executionFunctions[0] = ISessionKeyPermissionsPlugin.updateKeyPermissions.selector; - manifest.executionFunctions[1] = ISessionKeyPermissionsPlugin.registerKey.selector; - manifest.executionFunctions[2] = ISessionKeyPermissionsPlugin.rotateKey.selector; - - // Associate the owner's user op and runtime validator with permissions config - ManifestFunction memory ownerUserOpValidationFunction = ManifestFunction({ - functionType: ManifestAssociatedFunctionType.DEPENDENCY, - functionId: 0, // unused since it's a dependency - dependencyIndex: _MANIFEST_DEPENDENCY_INDEX_OWNER_USER_OP_VALIDATION - }); - manifest.userOpValidationFunctions = new ManifestAssociatedFunction[](3); - manifest.userOpValidationFunctions[0] = ManifestAssociatedFunction({ - executionSelector: this.updateKeyPermissions.selector, - associatedFunction: ownerUserOpValidationFunction - }); - manifest.userOpValidationFunctions[1] = ManifestAssociatedFunction({ - executionSelector: this.registerKey.selector, - associatedFunction: ownerUserOpValidationFunction - }); - manifest.userOpValidationFunctions[2] = ManifestAssociatedFunction({ - executionSelector: this.rotateKey.selector, - associatedFunction: ownerUserOpValidationFunction - }); - - ManifestFunction memory ownerRuntimeValidationFunction = ManifestFunction({ - functionType: ManifestAssociatedFunctionType.DEPENDENCY, - functionId: 0, // unused since it's a dependency - dependencyIndex: _MANIFEST_DEPENDENCY_INDEX_OWNER_RUNTIME_VALIDATION - }); - manifest.runtimeValidationFunctions = new ManifestAssociatedFunction[](3); - manifest.runtimeValidationFunctions[0] = ManifestAssociatedFunction({ - executionSelector: this.updateKeyPermissions.selector, - associatedFunction: ownerRuntimeValidationFunction - }); - manifest.runtimeValidationFunctions[1] = ManifestAssociatedFunction({ - executionSelector: this.registerKey.selector, - associatedFunction: ownerRuntimeValidationFunction - }); - manifest.runtimeValidationFunctions[2] = ManifestAssociatedFunction({ - executionSelector: this.rotateKey.selector, - associatedFunction: ownerRuntimeValidationFunction - }); - - // Apply the "enforcing" pre validation hook and pre exec hook - manifest.preUserOpValidationHooks = new ManifestAssociatedFunction[](1); - manifest.preUserOpValidationHooks[0] = ManifestAssociatedFunction({ - executionSelector: ISessionKeyPlugin.executeWithSessionKey.selector, - associatedFunction: ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: uint8(FunctionId.PRE_USER_OP_VALIDATION_HOOK_CHECK_PERMISSIONS), - dependencyIndex: 0 // Unused. - }) - }); - - manifest.executionHooks = new ManifestExecutionHook[](1); - manifest.executionHooks[0] = ManifestExecutionHook({ - executionSelector: ISessionKeyPlugin.executeWithSessionKey.selector, - preExecHook: ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: uint8(FunctionId.PRE_EXECUTION_HOOK_UPDATE_LIMITS), - dependencyIndex: 0 // Unused. - }), - postExecHook: ManifestFunction({ - functionType: ManifestAssociatedFunctionType.NONE, - functionId: 0, // Unused. - dependencyIndex: 0 // Unused. - }) - }); - - return manifest; - } - - /// @inheritdoc BasePlugin - function pluginMetadata() external pure virtual override returns (PluginMetadata memory) { - PluginMetadata memory metadata; - metadata.name = _NAME; - metadata.version = _VERSION; - metadata.author = _AUTHOR; - - // Permission strings - string memory modifyOwnershipPermission = "Modify Session Key Permissions"; - - // Permission descriptions - metadata.permissionDescriptors = new SelectorPermission[](1); - metadata.permissionDescriptors[0] = SelectorPermission({ - functionSelector: this.updateKeyPermissions.selector, - permissionDescription: modifyOwnershipPermission - }); - - return metadata; - } - - // ┏━━━━━━━━━━━━━━━┓ - // ┃ EIP-165 ┃ - // ┗━━━━━━━━━━━━━━━┛ - - /// @inheritdoc BasePlugin - function supportsInterface(bytes4 interfaceId) public view override returns (bool) { - return - interfaceId == type(ISessionKeyPermissionsPlugin).interfaceId || super.supportsInterface(interfaceId); - } - // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ // ┃ Internal / Private functions ┃ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - /// @dev A pre user op validation hook that checks the permissions of the key used to validate the user op. - function _checkUserOpPermissions(UserOperation calldata userOp) internal returns (uint256) { - // If not calling executeWithSessionKey, nothing to do. Return 0 as validation success. - if (bytes4(userOp.callData) != ISessionKeyPlugin.executeWithSessionKey.selector) return 0; - - // Decode the executions array and the session key from the user op's calldata - (Call[] memory calls, address sessionKey) = abi.decode(userOp.callData[4:], (Call[], address)); - - (SessionKeyData storage sessionKeyData, SessionKeyId keyId) = _loadSessionKey(msg.sender, sessionKey); + /// @dev A check to run during user op validation that checks the permissions of the session key used to + /// validate the user op. + function _checkUserOpPermissions(UserOperation calldata userOp, Call[] memory calls, address sessionKey) + internal + returns (uint256) + { + // This step does not need to assert that the key id is nonzero, since the user op signature check implies + // that. + SessionKeyId keyId = _sessionKeyIdOf(msg.sender, sessionKey); + SessionKeyData storage sessionKeyData = _sessionKeyDataOf(msg.sender, keyId); // The session key's start time is the max of the key-specific validAfter and any time restrictions imposed // by spending limits. uint48 currentValidAfter = sessionKeyData.validAfter; + uint48 validUntil = sessionKeyData.validUntil; uint256 nativeTokenSpend; uint256 callsLength = calls.length; @@ -289,6 +94,16 @@ contract SessionKeyPermissionsPlugin is ISessionKeyPermissionsPlugin, SessionKey } if (sessionKeyData.hasGasLimit) { + // Gas limit checking is the only type of permissions checking that has state changes performed during + // validation. This can potentially cause reputation damage to staked accounts if multiple user + // operations are accepted into the same bundle, then validation for one of the later operations fails + // due to the state change from the first. To protect from this, we require session keys to use their + // own address as the key portion of the user operation nonce field, in order to guarantee that they + // are used sequentially. + if (uint192(userOp.nonce >> 64) != uint192(uint160(sessionKey))) { + validationSuccess = false; + } + // Multiplier for the verification gas limit is 3 if there is a paymaster, 1 otherwise. // This is defined in EntryPoint v0.6.0, which uses the limit for the user op validation + paymaster // validation, then again for up to two more calls of `postOp`. Later versions of the EntryPoint may @@ -302,16 +117,6 @@ contract SessionKeyPermissionsPlugin is ISessionKeyPermissionsPlugin, SessionKey _checkAndUpdateGasLimitUsage(maxGasFee, sessionKeyData); validationSuccess = validationSuccess && gasLimitSuccess; currentValidAfter = _max(currentValidAfter, gasLimitValidAfter); - - // Gas limit checking is the only type of permissions checking that has state changes performed during - // validation. This can potentially cause reputation damage to staked accounts if multiple user - // operations are accepted into the same bundle, then validation for one of the later operations fails - // due to the state change from the first. To protect from this, we require session keys to use their - // own address as the key portion of the user operation nonce field, in order to guarantee that they - // are used sequentially. - if (uint192(userOp.nonce >> 64) != uint192(uint160(sessionKey))) { - validationSuccess = false; - } } if (sessionKeyData.hasRequiredPaymaster) { @@ -330,12 +135,12 @@ contract SessionKeyPermissionsPlugin is ISessionKeyPermissionsPlugin, SessionKey // otherwise a packed struct of the aggregator address (0 here), and two // 6-byte timestamps indicating the start and end times at which the op // is valid. - return uint160(!validationSuccess ? 1 : 0) | (uint256(sessionKeyData.validUntil) << 160) + return uint160(!validationSuccess ? 1 : 0) | (uint256(validUntil) << 160) | (uint256(currentValidAfter) << (208)); } - /// @dev Checks permissions on a per-call basis. Should be run in the pre user op validation hook once per - /// `Call` struct in the user op's calldata. + /// @dev Checks permissions on a per-call basis. Should be run during user op validation once per `Call` struct + /// in the user op's calldata. function _checkCallPermissions( ContractAccessControlType accessControlType, SessionKeyId keyId, @@ -366,17 +171,16 @@ contract SessionKeyPermissionsPlugin is ISessionKeyPermissionsPlugin, SessionKey } } - /// @dev Runs as a pre exec hook, and updates the spend limits of the session key in use - function _updateLimitsPreExec(address account, bytes calldata callData) internal { - // If not calling executeWithSessionKey, nothing to do. - if (bytes4(callData) != ISessionKeyPlugin.executeWithSessionKey.selector) return; - - (Call[] memory calls, address sessionKey) = abi.decode(callData[4:], (Call[], address)); + /// @dev Runs during execution to re-check and update the spend limits of the session key in use. + function _updateLimitsPreExec(address account, Call[] calldata calls, address sessionKey) internal { uint256 callsLength = calls.length; uint256 newNativeTokenUsage; - (SessionKeyData storage sessionKeyData, SessionKeyId keyId) = _loadSessionKey(account, sessionKey); + // This step does not need to assert that the key id is nonzero, since the user op signature check implies + // that. + SessionKeyId keyId = _sessionKeyIdOf(msg.sender, sessionKey); + SessionKeyData storage sessionKeyData = _sessionKeyDataOf(msg.sender, keyId); for (uint256 i = 0; i < callsLength;) { Call memory call = calls[i]; diff --git a/src/plugins/session/permissions/SessionKeyPermissionsBase.sol b/src/plugins/session/permissions/SessionKeyPermissionsBase.sol index f9e95cbd..e4594221 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsBase.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsBase.sol @@ -1,13 +1,15 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.21; -import {ISessionKeyPermissionsPlugin} from "./ISessionKeyPermissionsPlugin.sol"; +import {ISessionKeyPlugin} from "../ISessionKeyPlugin.sol"; import {PluginStorageLib, StoragePointer} from "../../../libraries/PluginStorageLib.sol"; -abstract contract SessionKeyPermissionsBase is ISessionKeyPermissionsPlugin { +abstract contract SessionKeyPermissionsBase is ISessionKeyPlugin { type SessionKeyId is bytes32; + // Implementation-internal structs not exposed by the external interface. + struct SessionKeyData { // Contract access control type ContractAccessControlType contractAccessControlType; @@ -87,6 +89,14 @@ abstract contract SessionKeyPermissionsBase is ISessionKeyPermissionsPlugin { // Storage fields mapping(address => uint256) internal _keyIdCounter; + // Internal Functions + + function _assertKeyExists(SessionKeyId id, address sessionKey) internal pure { + if (SessionKeyId.unwrap(id) == bytes32(0)) { + revert InvalidSessionKey(sessionKey); + } + } + function _sessionKeyIdOf(address associated, address sessionKey) internal view returns (SessionKeyId keyId) { uint256 prefixAndBatchIndex = uint256(bytes32(SESSION_KEY_ID_PREFIX)); bytes memory associatedStorageKey = @@ -98,12 +108,6 @@ abstract contract SessionKeyPermissionsBase is ISessionKeyPermissionsPlugin { } } - function _assertRegistered(SessionKeyId id, address sessionKey) internal pure { - if (SessionKeyId.unwrap(id) == bytes32(0)) { - revert KeyNotRegistered(sessionKey); - } - } - function _updateSessionKeyId(address associated, address sessionKey, SessionKeyId newId) internal { uint256 prefixAndBatchIndex = uint256(bytes32(SESSION_KEY_ID_PREFIX)); bytes memory associatedStorageKey = @@ -136,7 +140,7 @@ abstract contract SessionKeyPermissionsBase is ISessionKeyPermissionsPlugin { returns (SessionKeyData storage sessionKeyData, SessionKeyId keyId) { SessionKeyId id = _sessionKeyIdOf(associated, sessionKey); - _assertRegistered(id, sessionKey); + _assertKeyExists(id, sessionKey); return (_sessionKeyDataOf(associated, id), id); } diff --git a/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol b/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol index f3bc3a8d..de9bfff4 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.21; -import {ISessionKeyPermissionsPlugin} from "./ISessionKeyPermissionsPlugin.sol"; +import {ISessionKeyPlugin} from "../ISessionKeyPlugin.sol"; import {SessionKeyPermissionsBase} from "./SessionKeyPermissionsBase.sol"; abstract contract SessionKeyPermissionsLoupe is SessionKeyPermissionsBase { - /// @inheritdoc ISessionKeyPermissionsPlugin + /// @inheritdoc ISessionKeyPlugin function getAccessControlType(address account, address sessionKey) external view @@ -15,19 +15,19 @@ abstract contract SessionKeyPermissionsLoupe is SessionKeyPermissionsBase { return sessionKeyData.contractAccessControlType; } - /// @inheritdoc ISessionKeyPermissionsPlugin + /// @inheritdoc ISessionKeyPlugin function getAccessControlEntry(address account, address sessionKey, address contractAddress) external view returns (bool isOnList, bool checkSelectors) { SessionKeyId keyId = _sessionKeyIdOf(account, sessionKey); - _assertRegistered(keyId, sessionKey); + _assertKeyExists(keyId, sessionKey); ContractData storage contractData = _contractDataOf(account, keyId, contractAddress); return (contractData.isOnList, contractData.checkSelectors); } - /// @inheritdoc ISessionKeyPermissionsPlugin + /// @inheritdoc ISessionKeyPlugin function isSelectorOnAccessControlList( address account, address sessionKey, @@ -35,12 +35,12 @@ abstract contract SessionKeyPermissionsLoupe is SessionKeyPermissionsBase { bytes4 selector ) external view returns (bool isOnList) { SessionKeyId keyId = _sessionKeyIdOf(account, sessionKey); - _assertRegistered(keyId, sessionKey); + _assertKeyExists(keyId, sessionKey); FunctionData storage functionData = _functionDataOf(account, keyId, contractAddress, selector); return functionData.isOnList; } - /// @inheritdoc ISessionKeyPermissionsPlugin + /// @inheritdoc ISessionKeyPlugin function getKeyTimeRange(address account, address sessionKey) external view @@ -50,7 +50,7 @@ abstract contract SessionKeyPermissionsLoupe is SessionKeyPermissionsBase { return (sessionKeyData.validAfter, sessionKeyData.validUntil); } - /// @inheritdoc ISessionKeyPermissionsPlugin + /// @inheritdoc ISessionKeyPlugin function getNativeTokenSpendLimitInfo(address account, address sessionKey) external view @@ -74,7 +74,7 @@ abstract contract SessionKeyPermissionsLoupe is SessionKeyPermissionsBase { } } - /// @inheritdoc ISessionKeyPermissionsPlugin + /// @inheritdoc ISessionKeyPlugin function getERC20SpendLimitInfo(address account, address sessionKey, address token) external view @@ -91,15 +91,15 @@ abstract contract SessionKeyPermissionsLoupe is SessionKeyPermissionsBase { }); } - /// @inheritdoc ISessionKeyPermissionsPlugin + /// @inheritdoc ISessionKeyPlugin function getRequiredPaymaster(address account, address sessionKey) external view returns (address) { SessionKeyId id = _sessionKeyIdOf(account, sessionKey); - _assertRegistered(id, sessionKey); + _assertKeyExists(id, sessionKey); SessionKeyData storage sessionKeyData = _sessionKeyDataOf(account, id); return sessionKeyData.hasRequiredPaymaster ? sessionKeyData.requiredPaymaster : address(0); } - /// @inheritdoc ISessionKeyPermissionsPlugin + /// @inheritdoc ISessionKeyPlugin function getGasSpendLimit(address account, address sessionKey) external view diff --git a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol index 1e5e7f2e..2bd13ada 100644 --- a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol +++ b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol @@ -11,6 +11,8 @@ import {BasePlugin} from "../../../src/plugins/BasePlugin.sol"; import {IMultiOwnerPlugin} from "../../../src/plugins/owner/IMultiOwnerPlugin.sol"; import {MultiOwnerPlugin} from "../../../src/plugins/owner/MultiOwnerPlugin.sol"; import {ISessionKeyPlugin} from "../../../src/plugins/session/ISessionKeyPlugin.sol"; +import {ISessionKeyPermissionsUpdates} from + "../../../src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol"; import {SessionKeyPlugin} from "../../../src/plugins/session/SessionKeyPlugin.sol"; import {IEntryPoint} from "../../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../../src/interfaces/erc4337/UserOperation.sol"; @@ -39,6 +41,11 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { address payable recipient; + // Event re-declarations for use with `vm.expectEmit()` + event SessionKeyAdded(address indexed account, address indexed sessionKey, bytes32 indexed tag); + event SessionKeyRemoved(address indexed account, address indexed sessionKey); + event SessionKeyRotated(address indexed account, address indexed oldSessionKey, address indexed newSessionKey); + function setUp() public { entryPoint = IEntryPoint(address(new EntryPoint())); (owner1, owner1Key) = makeAddrAndKey("owner1"); @@ -84,70 +91,140 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { } function test_sessionKey_addKeySuccess() public { - address[] memory sessionKeysToAdd = new address[](1); - sessionKeysToAdd[0] = makeAddr("sessionKey1"); + address sessionKeyToAdd = makeAddr("sessionKey1"); + vm.expectEmit(true, true, true, true); + emit SessionKeyAdded(address(account1), sessionKeyToAdd, bytes32(0)); vm.prank(owner1); - SessionKeyPlugin(address(account1)).updateSessionKeys( - sessionKeysToAdd, new SessionKeyPlugin.SessionKeyToRemove[](0) - ); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKeyToAdd, bytes32(0)); - address[] memory sessionKeys = SessionKeyPlugin(address(account1)).getSessionKeys(); + // Check using all view methods + address[] memory sessionKeys = sessionKeyPlugin.sessionKeysOf(address(account1)); assertEq(sessionKeys.length, 1); - assertEq(sessionKeys[0], sessionKeysToAdd[0]); + assertEq(sessionKeys[0], sessionKeyToAdd); + assertTrue(sessionKeyPlugin.isSessionKeyOf(address(account1), sessionKeyToAdd)); } function test_sessionKey_addKeyFailure() public { vm.startPrank(owner1); - address[] memory sessionKeysToAdd = new address[](1); + // Zero address session key + address sessionKeyToAdd = address(0); vm.expectRevert(abi.encodeWithSelector(ISessionKeyPlugin.InvalidSessionKey.selector, address(0))); - SessionKeyPlugin(address(account1)).updateSessionKeys( - sessionKeysToAdd, new SessionKeyPlugin.SessionKeyToRemove[](0) - ); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKeyToAdd, bytes32(0)); - sessionKeysToAdd = new address[](2); - sessionKeysToAdd[0] = makeAddr("sessionKey1"); - sessionKeysToAdd[1] = sessionKeysToAdd[0]; - vm.expectRevert(abi.encodeWithSelector(ISessionKeyPlugin.InvalidSessionKey.selector, sessionKeysToAdd[0])); - SessionKeyPlugin(address(account1)).updateSessionKeys( - sessionKeysToAdd, new SessionKeyPlugin.SessionKeyToRemove[](0) - ); + // Duplicate session key + sessionKeyToAdd = makeAddr("sessionKey1"); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKeyToAdd, bytes32(0)); + vm.expectRevert(abi.encodeWithSelector(ISessionKeyPlugin.InvalidSessionKey.selector, sessionKeyToAdd)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKeyToAdd, bytes32(0)); + + // Check using all view methods + address[] memory sessionKeys = sessionKeyPlugin.sessionKeysOf(address(account1)); + assertEq(sessionKeys.length, 1); + assertEq(sessionKeys[0], sessionKeyToAdd); + assertTrue(sessionKeyPlugin.isSessionKeyOf(address(account1), sessionKeyToAdd)); } function test_sessionKey_addAndRemoveKeys() public { - address[] memory sessionKeysToAdd = new address[](2); - sessionKeysToAdd[0] = makeAddr("sessionKey1"); - sessionKeysToAdd[1] = makeAddr("sessionKey2"); + address sessionKey1 = makeAddr("sessionKey1"); + address sessionKey2 = makeAddr("sessionKey2"); - vm.prank(owner1); - SessionKeyPlugin(address(account1)).updateSessionKeys( - sessionKeysToAdd, new SessionKeyPlugin.SessionKeyToRemove[](0) + vm.startPrank(owner1); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey2, bytes32(0)); + vm.stopPrank(); + + // Check using all view methods + address[] memory sessionKeys = sessionKeyPlugin.sessionKeysOf(address(account1)); + assertEq(sessionKeys.length, 2); + assertEq(sessionKeys[0], sessionKey2); + assertEq(sessionKeys[1], sessionKey1); + assertTrue(sessionKeyPlugin.isSessionKeyOf(address(account1), sessionKey1)); + assertTrue(sessionKeyPlugin.isSessionKeyOf(address(account1), sessionKey2)); + + vm.expectEmit(true, true, true, true); + emit SessionKeyRemoved(address(account1), sessionKey1); + vm.startPrank(owner1); + SessionKeyPlugin(address(account1)).removeSessionKey( + sessionKey1, sessionKeyPlugin.findPredecessor(address(account1), sessionKey1) ); + vm.stopPrank(); - SessionKeyPlugin.SessionKeyToRemove[] memory sessionKeysToRemove = - new ISessionKeyPlugin.SessionKeyToRemove[](1); - sessionKeysToRemove[0] = ISessionKeyPlugin.SessionKeyToRemove({ - sessionKey: sessionKeysToAdd[0], - predecessor: bytes32(bytes20(sessionKeysToAdd[1])) - }); + // Check using all view methods + sessionKeys = sessionKeyPlugin.sessionKeysOf(address(account1)); + assertEq(sessionKeys.length, 1); + assertEq(sessionKeys[0], sessionKey2); + assertFalse(sessionKeyPlugin.isSessionKeyOf(address(account1), sessionKey1)); + assertTrue(sessionKeyPlugin.isSessionKeyOf(address(account1), sessionKey2)); + } + + function test_sessionKey_rotate_valid() public { + // Add the first key + address sessionKey1 = makeAddr("sessionKey1"); vm.prank(owner1); - SessionKeyPlugin(address(account1)).updateSessionKeys(new address[](0), sessionKeysToRemove); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); - address[] memory sessionKeys = SessionKeyPlugin(address(account1)).getSessionKeys(); + // Rotate to the second key + address sessionKey2 = makeAddr("sessionKey2"); + bytes32 predecessor = sessionKeyPlugin.findPredecessor(address(account1), sessionKey1); + vm.expectEmit(true, true, true, true); + emit SessionKeyRotated(address(account1), sessionKey1, sessionKey2); + vm.prank(owner1); + SessionKeyPlugin(address(account1)).rotateSessionKey(sessionKey1, predecessor, sessionKey2); + + // Check using all view methods + address[] memory sessionKeys = sessionKeyPlugin.sessionKeysOf(address(account1)); assertEq(sessionKeys.length, 1); - assertEq(sessionKeys[0], sessionKeysToAdd[1]); + assertEq(sessionKeys[0], sessionKey2); + assertFalse(sessionKeyPlugin.isSessionKeyOf(address(account1), sessionKey1)); + assertTrue(sessionKeyPlugin.isSessionKeyOf(address(account1), sessionKey2)); } - function test_sessionKey_useSessionKey() public { - address[] memory sessionKeysToAdd = new address[](1); - (address sessionKey, uint256 sessionKeyPrivate) = makeAddrAndKey("sessionKey1"); - sessionKeysToAdd[0] = sessionKey; + function test_sessionKey_rotate_existing() public { + // Add the session keys 1 and 2 + address sessionKey1 = makeAddr("sessionKey1"); + address sessionKey2 = makeAddr("sessionKey2"); + vm.startPrank(owner1); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey2, bytes32(0)); + vm.stopPrank(); + + // Attempt to rotate key 1 to key 2 + bytes32 predecessor = sessionKeyPlugin.findPredecessor(address(account1), sessionKey1); + vm.expectRevert(abi.encodeWithSelector(ISessionKeyPlugin.InvalidSessionKey.selector, sessionKey2)); + vm.prank(owner1); + SessionKeyPlugin(address(account1)).rotateSessionKey(sessionKey1, predecessor, sessionKey2); + } + function test_sessionKey_rotate_invalid() public { + // Add the first key + address sessionKey1 = makeAddr("sessionKey1"); vm.prank(owner1); - SessionKeyPlugin(address(account1)).updateSessionKeys( - sessionKeysToAdd, new SessionKeyPlugin.SessionKeyToRemove[](0) + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); + + // Attempt to rotate to the zero address + address zeroAddr = address(0); + bytes32 predecessor = sessionKeyPlugin.findPredecessor(address(account1), sessionKey1); + vm.expectRevert(abi.encodeWithSelector(ISessionKeyPlugin.InvalidSessionKey.selector, zeroAddr)); + vm.prank(owner1); + SessionKeyPlugin(address(account1)).rotateSessionKey(sessionKey1, predecessor, zeroAddr); + } + + function test_sessionKey_useSessionKey() public { + (address sessionKey1, uint256 sessionKeyPrivate) = makeAddrAndKey("sessionKey1"); + + vm.startPrank(owner1); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); + // Disable the allowlist and native token spend checking + bytes[] memory permissionUpdates = new bytes[](2); + permissionUpdates[0] = abi.encodeCall( + ISessionKeyPermissionsUpdates.setAccessListType, (ISessionKeyPlugin.ContractAccessControlType.NONE) ); + permissionUpdates[1] = + abi.encodeCall(ISessionKeyPermissionsUpdates.setNativeTokenSpendLimit, (type(uint256).max, 0)); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, permissionUpdates); + vm.stopPrank(); Call[] memory calls = new Call[](1); calls[0] = Call({target: recipient, value: 1 wei, data: ""}); @@ -157,7 +234,7 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { nonce: 0, initCode: "", callData: abi.encodeCall( - ISessionKeyPlugin(address(sessionKeyPlugin)).executeWithSessionKey, (calls, sessionKey) + ISessionKeyPlugin(address(sessionKeyPlugin)).executeWithSessionKey, (calls, sessionKey1) ), callGasLimit: CALL_GAS_LIMIT, verificationGasLimit: VERIFICATION_GAS_LIMIT, @@ -181,20 +258,16 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { } function test_sessionKey_useSessionKey_failInRuntime() public { - address[] memory sessionKeysToAdd = new address[](1); - (address sessionKey,) = makeAddrAndKey("sessionKey1"); - sessionKeysToAdd[0] = sessionKey; + (address sessionKey1,) = makeAddrAndKey("sessionKey1"); vm.startPrank(owner1); - SessionKeyPlugin(address(account1)).updateSessionKeys( - sessionKeysToAdd, new SessionKeyPlugin.SessionKeyToRemove[](0) - ); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); Call[] memory calls = new Call[](1); calls[0] = Call({target: recipient, value: 1 wei, data: ""}); vm.expectRevert(abi.encodeWithSelector(UpgradeableModularAccount.AlwaysDenyRule.selector)); - SessionKeyPlugin(address(account1)).executeWithSessionKey(calls, sessionKey); + SessionKeyPlugin(address(account1)).executeWithSessionKey(calls, sessionKey1); } function testFuzz_sessionKey_userOpValidation_valid(uint16 seed) public { @@ -324,62 +397,56 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { // getPredecessor test case with sentinel value as predecessor function test_sessionKey_getPredecessor_sentinel() public { - address[] memory sessionKeysToAdd = new address[](2); - sessionKeysToAdd[0] = makeAddr("sessionKey1"); - sessionKeysToAdd[1] = makeAddr("sessionKey2"); + address sessionKey1 = makeAddr("sessionKey1"); + address sessionKey2 = makeAddr("sessionKey2"); - vm.prank(owner1); - SessionKeyPlugin(address(account1)).updateSessionKeys( - sessionKeysToAdd, new SessionKeyPlugin.SessionKeyToRemove[](0) - ); + vm.startPrank(owner1); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey2, bytes32(0)); - SessionKeyPlugin.SessionKeyToRemove[] memory sessionKeysToRemove = - new ISessionKeyPlugin.SessionKeyToRemove[](1); - sessionKeysToRemove[0] = ISessionKeyPlugin.SessionKeyToRemove({ - sessionKey: sessionKeysToAdd[0], - predecessor: sessionKeyPlugin.findPredecessor(address(account1), sessionKeysToAdd[0]) - }); - vm.prank(owner1); - SessionKeyPlugin(address(account1)).updateSessionKeys(new address[](0), sessionKeysToRemove); + bytes32 predecessor = sessionKeyPlugin.findPredecessor(address(account1), sessionKey2); + assertEq(predecessor, bytes32(uint256(1))); - address[] memory sessionKeys = SessionKeyPlugin(address(account1)).getSessionKeys(); + SessionKeyPlugin(address(account1)).removeSessionKey(sessionKey2, predecessor); + vm.stopPrank(); + + // Check using all view methods + address[] memory sessionKeys = sessionKeyPlugin.sessionKeysOf(address(account1)); assertEq(sessionKeys.length, 1); - assertEq(sessionKeys[0], sessionKeysToAdd[1]); + assertEq(sessionKeys[0], sessionKey1); + assertTrue(sessionKeyPlugin.isSessionKeyOf(address(account1), sessionKey1)); + assertFalse(sessionKeyPlugin.isSessionKeyOf(address(account1), sessionKey2)); } // getPredecessor test case with address value as predecessor function test_sessionKey_getPredecessor_address() public { - address[] memory sessionKeysToAdd = new address[](2); - sessionKeysToAdd[0] = makeAddr("sessionKey1"); - sessionKeysToAdd[1] = makeAddr("sessionKey2"); + address sessionKey1 = makeAddr("sessionKey1"); + address sessionKey2 = makeAddr("sessionKey2"); - vm.prank(owner1); - SessionKeyPlugin(address(account1)).updateSessionKeys( - sessionKeysToAdd, new SessionKeyPlugin.SessionKeyToRemove[](0) - ); + vm.startPrank(owner1); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey2, bytes32(0)); - SessionKeyPlugin.SessionKeyToRemove[] memory sessionKeysToRemove = - new ISessionKeyPlugin.SessionKeyToRemove[](1); - sessionKeysToRemove[0] = ISessionKeyPlugin.SessionKeyToRemove({ - sessionKey: sessionKeysToAdd[1], - predecessor: sessionKeyPlugin.findPredecessor(address(account1), sessionKeysToAdd[1]) - }); - vm.prank(owner1); - SessionKeyPlugin(address(account1)).updateSessionKeys(new address[](0), sessionKeysToRemove); + bytes32 predecessor = sessionKeyPlugin.findPredecessor(address(account1), sessionKey1); + assertEq(predecessor, bytes32(bytes20(sessionKey2))); - address[] memory sessionKeys = SessionKeyPlugin(address(account1)).getSessionKeys(); + SessionKeyPlugin(address(account1)).removeSessionKey(sessionKey1, predecessor); + vm.stopPrank(); + + // Check using all view methods + address[] memory sessionKeys = sessionKeyPlugin.sessionKeysOf(address(account1)); assertEq(sessionKeys.length, 1); - assertEq(sessionKeys[0], sessionKeysToAdd[0]); + assertEq(sessionKeys[0], sessionKey2); + assertTrue(sessionKeyPlugin.isSessionKeyOf(address(account1), sessionKey2)); + assertFalse(sessionKeyPlugin.isSessionKeyOf(address(account1), sessionKey1)); } function test_sessionKey_getPredecessor_missing() public { address[] memory sessionKeysToAdd = new address[](1); sessionKeysToAdd[0] = makeAddr("sessionKey1"); - vm.prank(owner1); - SessionKeyPlugin(address(account1)).updateSessionKeys( - sessionKeysToAdd, new SessionKeyPlugin.SessionKeyToRemove[](0) - ); + vm.startPrank(owner1); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKeysToAdd[0], bytes32(0)); address key2 = makeAddr("sessionKey2"); vm.expectRevert(abi.encodeWithSelector(ISessionKeyPlugin.SessionKeyNotFound.selector, key2)); @@ -393,9 +460,7 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { sessionKeysToAdd[0] = makeAddr("sessionKey1"); vm.prank(owner1); - SessionKeyPlugin(address(account1)).updateSessionKeys( - sessionKeysToAdd, new SessionKeyPlugin.SessionKeyToRemove[](0) - ); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKeysToAdd[0], bytes32(0)); assertFalse(sessionKeyPlugin.isSessionKeyOf(address(account1), address(1))); } @@ -409,9 +474,19 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { (sessionKeysToAdd[i], privateKeys[i]) = makeAddrAndKey(string.concat("sessionKey", vm.toString(i))); } - vm.prank(owner1); - SessionKeyPlugin(address(account1)).updateSessionKeys( - sessionKeysToAdd, new SessionKeyPlugin.SessionKeyToRemove[](0) + // To disable the allowlist and native token spend checking + bytes[] memory permissionUpdates = new bytes[](2); + permissionUpdates[0] = abi.encodeCall( + ISessionKeyPermissionsUpdates.setAccessListType, (ISessionKeyPlugin.ContractAccessControlType.NONE) ); + permissionUpdates[1] = + abi.encodeCall(ISessionKeyPermissionsUpdates.setNativeTokenSpendLimit, (type(uint256).max, 0)); + + vm.startPrank(owner1); + for (uint256 i = 0; i < addressCount; i++) { + SessionKeyPlugin(address(account1)).addSessionKey(sessionKeysToAdd[i], bytes32(0)); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKeysToAdd[i], permissionUpdates); + } + vm.stopPrank(); } } diff --git a/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol b/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol index 5df0f03b..a34470c3 100644 --- a/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol @@ -11,12 +11,8 @@ import {IMultiOwnerPlugin} from "../../../../src/plugins/owner/IMultiOwnerPlugin import {MultiOwnerPlugin} from "../../../../src/plugins/owner/MultiOwnerPlugin.sol"; import {ISessionKeyPlugin} from "../../../../src/plugins/session/ISessionKeyPlugin.sol"; import {SessionKeyPlugin} from "../../../../src/plugins/session/SessionKeyPlugin.sol"; -import {ISessionKeyPermissionsPlugin} from - "../../../../src/plugins/session/permissions/ISessionKeyPermissionsPlugin.sol"; import {ISessionKeyPermissionsUpdates} from "../../../../src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol"; -import {SessionKeyPermissionsPlugin} from - "../../../../src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol"; import {IEntryPoint} from "../../../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../../../src/interfaces/erc4337/UserOperation.sol"; import {IPluginManager} from "../../../../src/interfaces/IPluginManager.sol"; @@ -35,7 +31,6 @@ contract SessionKeyERC20SpendLimitsTest is Test { MultiOwnerPlugin public multiOwnerPlugin; MultiOwnerMSCAFactory public factory; SessionKeyPlugin sessionKeyPlugin; - SessionKeyPermissionsPlugin sessionKeyPermissionsPlugin; address owner1; uint256 owner1Key; @@ -78,7 +73,6 @@ contract SessionKeyERC20SpendLimitsTest is Test { vm.deal(address(account1), 100 ether); sessionKeyPlugin = new SessionKeyPlugin(); - sessionKeyPermissionsPlugin = new SessionKeyPermissionsPlugin(); bytes32 manifestHash = keccak256(abi.encode(sessionKeyPlugin.pluginManifest())); FunctionReference[] memory dependencies = new FunctionReference[](2); @@ -97,40 +91,19 @@ contract SessionKeyERC20SpendLimitsTest is Test { injectedHooks: new IPluginManager.InjectedHook[](0) }); - manifestHash = keccak256(abi.encode(sessionKeyPermissionsPlugin.pluginManifest())); - // Can reuse the same dependencies for this installation, because the requirements are the same. - vm.prank(owner1); - account1.installPlugin({ - plugin: address(sessionKeyPermissionsPlugin), - manifestHash: manifestHash, - pluginInitData: "", - dependencies: dependencies, - injectedHooks: new IPluginManager.InjectedHook[](0) - }); - // Create and add a session key (sessionKey1, sessionKey1Private) = makeAddrAndKey("sessionKey1"); - address[] memory sessionKeysToAdd = new address[](1); - sessionKeysToAdd[0] = sessionKey1; - - vm.prank(owner1); - SessionKeyPlugin(address(account1)).updateSessionKeys( - sessionKeysToAdd, new SessionKeyPlugin.SessionKeyToRemove[](0) - ); - - // Register the session key with the permissions plugin vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).registerKey(sessionKey1, 0); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); // Disable the allowlist bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall( - ISessionKeyPermissionsUpdates.setAccessListType, - (ISessionKeyPermissionsPlugin.ContractAccessControlType.NONE) + ISessionKeyPermissionsUpdates.setAccessListType, (ISessionKeyPlugin.ContractAccessControlType.NONE) ); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Create recipients' addresses to receive the tokens recipient1 = makeAddr("recipient1"); @@ -144,13 +117,13 @@ contract SessionKeyERC20SpendLimitsTest is Test { function test_sessionKeyERC20SpendLimits_validateSetUp() public { // Check that the session key is registered - assertTrue(SessionKeyPlugin(address(account1)).isSessionKey(sessionKey1)); + assertTrue(sessionKeyPlugin.isSessionKeyOf(address(account1), sessionKey1)); // Check that the session key is registered with the permissions plugin and has its allowlist set up // correctly assertTrue( - sessionKeyPermissionsPlugin.getAccessControlType(address(account1), sessionKey1) - == ISessionKeyPermissionsPlugin.ContractAccessControlType.NONE + sessionKeyPlugin.getAccessControlType(address(account1), sessionKey1) + == ISessionKeyPlugin.ContractAccessControlType.NONE ); } @@ -167,8 +140,8 @@ contract SessionKeyERC20SpendLimitsTest is Test { vm.warp(timestamp); // Assert that the limit starts out unset - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, token); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo = + sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, token); assertFalse(spendLimitInfo.hasLimit); assertEq(spendLimitInfo.limit, 0); @@ -180,10 +153,10 @@ contract SessionKeyERC20SpendLimitsTest is Test { updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setERC20SpendLimit, (token, limit, refreshInterval)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Verify the limit can be retrieved - spendLimitInfo = sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, token); + spendLimitInfo = sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, token); if (limit == type(uint256).max) { // If the limit is "set" to this value, it is just removed. @@ -208,9 +181,9 @@ contract SessionKeyERC20SpendLimitsTest is Test { function test_sessionKeyERC20SpendLimits_tokenAddressZeroFails() public { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setERC20SpendLimit, (address(0), 1000, 0)); - vm.expectRevert(abi.encodeWithSelector(ISessionKeyPermissionsPlugin.InvalidToken.selector)); + vm.expectRevert(abi.encodeWithSelector(ISessionKeyPlugin.InvalidToken.selector)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); } function test_sessionKeyERC20SpendLimits_enforceLimit_none_basic() public { @@ -222,7 +195,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setERC20SpendLimit, (address(token1), 0 ether, 0 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a user op that spends 1 wei, should fail Call[] memory calls = new Call[](1); @@ -252,7 +225,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setERC20SpendLimit, (address(token1), 0 ether, 0 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // run a multi-execution user op that spends 0 wei, should succeed. Call[] memory calls = new Call[](3); @@ -279,7 +252,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setERC20SpendLimit, (address(token1), 1 ether, 0 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a user op that spends 1 wei, should succeed Call[] memory calls = new Call[](1); @@ -290,8 +263,8 @@ contract SessionKeyERC20SpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit is updated - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo = + sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 1 wei); @@ -311,7 +284,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setERC20SpendLimit, (address(token1), 1 ether, 0 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a user op that spends 1 wei, should succeed Call[] memory calls = new Call[](1); @@ -322,8 +295,8 @@ contract SessionKeyERC20SpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit is updated - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo = + sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 1 wei); @@ -339,8 +312,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit is not updated, and remains the same as before - spendLimitInfo = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + spendLimitInfo = sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 1 wei); @@ -358,7 +330,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setERC20SpendLimit, (address(token1), 1 ether, 0 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a user op that spends 3 wei, should succeed Call[] memory calls = new Call[](3); @@ -374,8 +346,8 @@ contract SessionKeyERC20SpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit is updated - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo = + sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 3 wei); assertEq(spendLimitInfo.refreshInterval, 0); @@ -396,7 +368,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setERC20SpendLimit, (address(token1), 1 ether, 0 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a user op that spends 1 ether, should succeed Call[] memory calls = new Call[](1); @@ -407,8 +379,8 @@ contract SessionKeyERC20SpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit is updated - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo = + sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 1 ether); assertEq(spendLimitInfo.refreshInterval, 0); @@ -425,7 +397,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setERC20SpendLimit, (address(token1), 1 ether, 0 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a user op that spends 3 wei, should succeed Call[] memory calls = new Call[](3); @@ -442,8 +414,8 @@ contract SessionKeyERC20SpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit is updated - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo = + sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 6 wei); assertEq(spendLimitInfo.refreshInterval, 0); @@ -460,7 +432,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setERC20SpendLimit, (address(token1), 1 ether, 0 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a user op that spends 3 wei, should succeed Call[] memory calls = new Call[](3); @@ -479,8 +451,8 @@ contract SessionKeyERC20SpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit is updated - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo = + sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 3 wei); assertEq(spendLimitInfo.refreshInterval, 0); @@ -500,7 +472,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { updates[1] = abi.encodeCall(ISessionKeyPermissionsUpdates.setERC20SpendLimit, (address(token2), 1 ether, 0 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a user op that spends 3 wei, should succeed Call[] memory calls = new Call[](3); @@ -519,10 +491,10 @@ contract SessionKeyERC20SpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit is updated - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo1 = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo2 = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token2)); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo1 = + sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo2 = + sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token2)); assertEq(spendLimitInfo1.limit, 1 ether); assertEq(spendLimitInfo1.limitUsed, 1 wei); assertEq(spendLimitInfo2.limit, 1 ether); @@ -538,7 +510,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setERC20SpendLimit, (address(token1), 1 wei, 0 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a user op that should fail due to exceeding limit Call[] memory calls = new Call[](2); @@ -552,8 +524,8 @@ contract SessionKeyERC20SpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit is NOT updated - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo = + sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); assertEq(spendLimitInfo.limit, 1 wei); // limit used should be 0 as all action failed. assertEq(spendLimitInfo.limitUsed, 0 wei); @@ -568,7 +540,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setERC20SpendLimit, (address(token1), 1 wei, 0 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a user op that should fail due to exceeding limit Call[] memory calls = new Call[](3); @@ -587,8 +559,8 @@ contract SessionKeyERC20SpendLimitsTest is Test { vm.expectCall(address(token1), 0 wei, calls[2].data, 0); _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit is NOT updated - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo = + sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); assertEq(spendLimitInfo.limit, 1 wei); // limit used should be 0 as all action failed. assertEq(spendLimitInfo.limitUsed, 0 wei); @@ -603,7 +575,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setERC20SpendLimit, (address(token1), 1 wei, 0 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a user op that should fail due to exceeding limit Call[] memory calls = new Call[](3); @@ -622,8 +594,8 @@ contract SessionKeyERC20SpendLimitsTest is Test { vm.expectCall(address(token1), 0 wei, calls[1].data, 0); _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit is NOT updated - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo = + sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); assertEq(spendLimitInfo.limit, 1 wei); // limit used should be 0 as all action failed. assertEq(spendLimitInfo.limitUsed, 0 wei); @@ -641,7 +613,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { updates[1] = abi.encodeCall(ISessionKeyPermissionsUpdates.setERC20SpendLimit, (address(token2), 1 wei, 0 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a user op that should fail due to exceeding limit Call[] memory calls = new Call[](3); @@ -660,10 +632,10 @@ contract SessionKeyERC20SpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit is NOT updated - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo1 = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo2 = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token2)); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo1 = + sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo2 = + sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token2)); assertEq(spendLimitInfo1.limit, 1 wei); // limit used should be 0 as all action failed. assertEq(spendLimitInfo1.limitUsed, 0 wei); @@ -685,7 +657,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setERC20SpendLimit, (address(token1), 1 ether, 1 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a user op that spends 1 wei, should succeed Call[] memory calls = new Call[](1); @@ -695,8 +667,8 @@ contract SessionKeyERC20SpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit and last used time is updated - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo = + sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 1 wei); assertEq(spendLimitInfo.refreshInterval, 1 days); @@ -709,8 +681,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit and last used time is NOT updated - spendLimitInfo = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + spendLimitInfo = sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 1 wei); assertEq(spendLimitInfo.refreshInterval, 1 days); @@ -726,8 +697,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit is now updated and the last used timestamp is set. - spendLimitInfo = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + spendLimitInfo = sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 1 ether); assertEq(spendLimitInfo.refreshInterval, 1 days); @@ -747,7 +717,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setERC20SpendLimit, (address(token1), 1 ether, 1 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a user op that spends 1 wei, should succeed Call[] memory calls = new Call[](2); @@ -759,8 +729,8 @@ contract SessionKeyERC20SpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit and last used time is updated - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo = + sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 2 wei); assertEq(spendLimitInfo.refreshInterval, 1 days); @@ -773,8 +743,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit and last used time is NOT updated - spendLimitInfo = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + spendLimitInfo = sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 2 wei); assertEq(spendLimitInfo.refreshInterval, 1 days); @@ -798,8 +767,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit is now updated and the last used timestamp is set. - spendLimitInfo = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + spendLimitInfo = sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 1 ether); assertEq(spendLimitInfo.refreshInterval, 1 days); @@ -819,7 +787,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setERC20SpendLimit, (address(token1), 1 ether, 1 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a user op that spends 1 wei and approve 1 wei, should succeed Call[] memory calls = new Call[](2); @@ -831,8 +799,8 @@ contract SessionKeyERC20SpendLimitsTest is Test { vm.expectCall(address(token1), 0 wei, calls[1].data, 1); _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit and last used time is updated - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo = + sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 2 wei); assertEq(spendLimitInfo.refreshInterval, 1 days); @@ -844,8 +812,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { vm.expectCall(address(token1), 0 wei, calls[0].data, 0); _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit and last used time is NOT updated - spendLimitInfo = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + spendLimitInfo = sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 2 wei); assertEq(spendLimitInfo.refreshInterval, 1 days); @@ -869,8 +836,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { vm.expectCall(address(token1), 0 wei, calls[0].data, 2); _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit is now updated and the last used timestamp is set. - spendLimitInfo = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + spendLimitInfo = sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 1 ether); assertEq(spendLimitInfo.refreshInterval, 1 days); @@ -890,7 +856,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setERC20SpendLimit, (address(token1), 1 ether, 1 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a user op that spends within limit, should succeed Call[] memory calls = new Call[](2); @@ -902,8 +868,8 @@ contract SessionKeyERC20SpendLimitsTest is Test { vm.expectCall(address(token1), 0 wei, calls[1].data, 1); _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit and last used time is updated - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo = + sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 3 wei); assertEq(spendLimitInfo.refreshInterval, 1 days); @@ -915,8 +881,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { vm.expectCall(address(token1), 0 wei, calls[0].data, 0); _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit and last used time is NOT updated - spendLimitInfo = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + spendLimitInfo = sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 3 wei); assertEq(spendLimitInfo.refreshInterval, 1 days); @@ -940,8 +905,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { vm.expectCall(address(token1), 0 wei, calls[1].data, 1); _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit is now updated and the last used timestamp is set. - spendLimitInfo = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + spendLimitInfo = sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 1 ether); assertEq(spendLimitInfo.refreshInterval, 1 days); @@ -964,7 +928,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { updates[1] = abi.encodeCall(ISessionKeyPermissionsUpdates.setERC20SpendLimit, (address(token2), 1 ether, 10 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a user op that spends max limit in interval, should succeed Call[] memory calls = new Call[](2); @@ -977,10 +941,10 @@ contract SessionKeyERC20SpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit and last used time is updated - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo1 = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo2 = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token2)); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo1 = + sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo2 = + sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token2)); assertEq(spendLimitInfo1.limit, 1 ether); assertEq(spendLimitInfo1.limitUsed, 1 ether); assertEq(spendLimitInfo2.limit, 1 ether); @@ -1000,10 +964,8 @@ contract SessionKeyERC20SpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit and last used time is NOT updated - spendLimitInfo1 = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); - spendLimitInfo2 = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token2)); + spendLimitInfo1 = sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + spendLimitInfo2 = sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token2)); assertEq(spendLimitInfo1.limit, 1 ether); assertEq(spendLimitInfo1.limitUsed, 1 ether); assertEq(spendLimitInfo1.lastUsedTime, time0); @@ -1024,10 +986,8 @@ contract SessionKeyERC20SpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit is now updated and the last used timestamp is set. - spendLimitInfo1 = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); - spendLimitInfo2 = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token2)); + spendLimitInfo1 = sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + spendLimitInfo2 = sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token2)); assertEq(spendLimitInfo1.limit, 1 ether); assertEq(spendLimitInfo1.limitUsed, 1 ether); assertEq(spendLimitInfo1.lastUsedTime, time0 + 10 days); @@ -1049,7 +1009,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setERC20SpendLimit, (address(token1), 1 ether, 1 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a user op that spends 0.5 ether, should succeed Call[] memory calls = new Call[](1); @@ -1062,8 +1022,8 @@ contract SessionKeyERC20SpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit and last used time is updated - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo = + sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 0.5 ether); assertEq(spendLimitInfo.refreshInterval, 1 days); @@ -1082,8 +1042,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that limits are not updated - spendLimitInfo = - sessionKeyPermissionsPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); + spendLimitInfo = sessionKeyPlugin.getERC20SpendLimitInfo(address(account1), sessionKey1, address(token1)); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 0.5 ether); assertEq(spendLimitInfo.refreshInterval, 1 days); diff --git a/test/plugin/session/permissions/SessionKeyGasLimits.t.sol b/test/plugin/session/permissions/SessionKeyGasLimits.t.sol index 39b741c6..afbabb81 100644 --- a/test/plugin/session/permissions/SessionKeyGasLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyGasLimits.t.sol @@ -11,12 +11,8 @@ import {IMultiOwnerPlugin} from "../../../../src/plugins/owner/IMultiOwnerPlugin import {MultiOwnerPlugin} from "../../../../src/plugins/owner/MultiOwnerPlugin.sol"; import {ISessionKeyPlugin} from "../../../../src/plugins/session/ISessionKeyPlugin.sol"; import {SessionKeyPlugin} from "../../../../src/plugins/session/SessionKeyPlugin.sol"; -import {ISessionKeyPermissionsPlugin} from - "../../../../src/plugins/session/permissions/ISessionKeyPermissionsPlugin.sol"; import {ISessionKeyPermissionsUpdates} from "../../../../src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol"; -import {SessionKeyPermissionsPlugin} from - "../../../../src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol"; import {IEntryPoint} from "../../../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../../../src/interfaces/erc4337/UserOperation.sol"; import {IPluginManager} from "../../../../src/interfaces/IPluginManager.sol"; @@ -34,7 +30,6 @@ contract SessionKeyGasLimitsTest is Test { MultiOwnerPlugin public multiOwnerPlugin; MultiOwnerMSCAFactory public factory; SessionKeyPlugin sessionKeyPlugin; - SessionKeyPermissionsPlugin sessionKeyPermissionsPlugin; address owner1; uint256 owner1Key; @@ -69,7 +64,6 @@ contract SessionKeyGasLimitsTest is Test { vm.deal(address(account1), 100 ether); sessionKeyPlugin = new SessionKeyPlugin(); - sessionKeyPermissionsPlugin = new SessionKeyPermissionsPlugin(); bytes32 manifestHash = keccak256(abi.encode(sessionKeyPlugin.pluginManifest())); FunctionReference[] memory dependencies = new FunctionReference[](2); @@ -88,47 +82,26 @@ contract SessionKeyGasLimitsTest is Test { injectedHooks: new IPluginManager.InjectedHook[](0) }); - manifestHash = keccak256(abi.encode(sessionKeyPermissionsPlugin.pluginManifest())); - // Can reuse the same dependencies for this installation, because the requirements are the same. - vm.prank(owner1); - account1.installPlugin({ - plugin: address(sessionKeyPermissionsPlugin), - manifestHash: manifestHash, - pluginInitData: "", - dependencies: dependencies, - injectedHooks: new IPluginManager.InjectedHook[](0) - }); - // Create and add a session key (sessionKey1, sessionKey1Private) = makeAddrAndKey("sessionKey1"); - address[] memory sessionKeysToAdd = new address[](1); - sessionKeysToAdd[0] = sessionKey1; - - vm.prank(owner1); - SessionKeyPlugin(address(account1)).updateSessionKeys( - sessionKeysToAdd, new SessionKeyPlugin.SessionKeyToRemove[](0) - ); - - // Register the session key with the permissions plugin vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).registerKey(sessionKey1, 0); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall( - ISessionKeyPermissionsUpdates.setAccessListType, - (ISessionKeyPermissionsPlugin.ContractAccessControlType.NONE) + ISessionKeyPermissionsUpdates.setAccessListType, (ISessionKeyPlugin.ContractAccessControlType.NONE) ); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); } function testFuzz_sessionKeyGasLimits_setLimits(uint256 limit, uint48 interval, uint48 timestamp) public { vm.warp(timestamp); // Assert that the limit starts out unset - (ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo,) = - sessionKeyPermissionsPlugin.getGasSpendLimit(address(account1), sessionKey1); + (ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo,) = + sessionKeyPlugin.getGasSpendLimit(address(account1), sessionKey1); assertFalse(spendLimitInfo.hasLimit); assertEq(spendLimitInfo.limit, 0); @@ -139,10 +112,10 @@ contract SessionKeyGasLimitsTest is Test { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setGasSpendLimit, (limit, interval)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Verify that the limit is set - (spendLimitInfo,) = sessionKeyPermissionsPlugin.getGasSpendLimit(address(account1), sessionKey1); + (spendLimitInfo,) = sessionKeyPlugin.getGasSpendLimit(address(account1), sessionKey1); if (limit == type(uint256).max) { // If the limit is "set" to this value, it is just removed. @@ -169,7 +142,7 @@ contract SessionKeyGasLimitsTest is Test { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setGasSpendLimit, (0 ether, 0 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // A user op spending any gas should be rejected at this stage _runSessionKeyUserOp( @@ -197,14 +170,14 @@ contract SessionKeyGasLimitsTest is Test { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setGasSpendLimit, (1 ether, 0 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // A basic user op using 0.6 ether in gas should succeed _runSessionKeyUserOp(100_000, 500_000, 1_000 gwei, 0.6 ether, sessionKey1Private, ""); // This usage update should be reflected in the limits - (ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo,) = - sessionKeyPermissionsPlugin.getGasSpendLimit(address(account1), sessionKey1); + (ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo,) = + sessionKeyPlugin.getGasSpendLimit(address(account1), sessionKey1); assertTrue(spendLimitInfo.hasLimit); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 0.6 ether); @@ -228,7 +201,7 @@ contract SessionKeyGasLimitsTest is Test { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setGasSpendLimit, (1 ether, 0 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); UserOperation[] memory userOps = new UserOperation[](2); userOps[0] = _generateAndSignUserOp( @@ -243,7 +216,7 @@ contract SessionKeyGasLimitsTest is Test { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setGasSpendLimit, (1 ether, 0 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // A basic user op using 1.2 ether in gas should fail _runSessionKeyUserOp( @@ -260,7 +233,7 @@ contract SessionKeyGasLimitsTest is Test { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setGasSpendLimit, (1 ether, 0 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Construct two user ops that, when bundled together, spend 0.8 ether UserOperation[] memory userOps = new UserOperation[](2); @@ -275,8 +248,8 @@ contract SessionKeyGasLimitsTest is Test { entryPoint.handleOps(userOps, beneficiary); // This usage update should be reflected in the limits - (ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo,) = - sessionKeyPermissionsPlugin.getGasSpendLimit(address(account1), sessionKey1); + (ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo,) = + sessionKeyPlugin.getGasSpendLimit(address(account1), sessionKey1); assertTrue(spendLimitInfo.hasLimit); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 0.8 ether); @@ -288,7 +261,7 @@ contract SessionKeyGasLimitsTest is Test { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setGasSpendLimit, (1 ether, 0 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Construct two user ops that, when bundled together, spend 1.6 ether UserOperation[] memory userOps = new UserOperation[](2); @@ -305,8 +278,8 @@ contract SessionKeyGasLimitsTest is Test { entryPoint.handleOps(userOps, beneficiary); // The lack of usage update should be reflected in the limits - (ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo,) = - sessionKeyPermissionsPlugin.getGasSpendLimit(address(account1), sessionKey1); + (ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo,) = + sessionKeyPlugin.getGasSpendLimit(address(account1), sessionKey1); assertTrue(spendLimitInfo.hasLimit); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 0 ether); @@ -322,14 +295,14 @@ contract SessionKeyGasLimitsTest is Test { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setGasSpendLimit, (1 ether, 1 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // A basic user op using 0.6 ether in gas should succeed _runSessionKeyUserOp(100_000, 500_000, 1_000 gwei, 0.6 ether, sessionKey1Private, ""); // This usage update should be reflected in the limits - (ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo,) = - sessionKeyPermissionsPlugin.getGasSpendLimit(address(account1), sessionKey1); + (ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo,) = + sessionKeyPlugin.getGasSpendLimit(address(account1), sessionKey1); assertTrue(spendLimitInfo.hasLimit); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 0.6 ether); @@ -362,14 +335,14 @@ contract SessionKeyGasLimitsTest is Test { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setGasSpendLimit, (1 ether, 1 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // A basic user op using 0.6 ether in gas should succeed _runSessionKeyUserOp(100_000, 500_000, 1_000 gwei, 0.6 ether, sessionKey1Private, ""); // This usage update should be reflected in the limits - (ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo,) = - sessionKeyPermissionsPlugin.getGasSpendLimit(address(account1), sessionKey1); + (ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo,) = + sessionKeyPlugin.getGasSpendLimit(address(account1), sessionKey1); assertTrue(spendLimitInfo.hasLimit); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 0.6 ether); @@ -393,7 +366,7 @@ contract SessionKeyGasLimitsTest is Test { _runSessionKeyUserOp(100_000, 500_000, 1_000 gwei, 0.6 ether, sessionKey1Private, ""); // This usage update should be reflected in the limits - (spendLimitInfo,) = sessionKeyPermissionsPlugin.getGasSpendLimit(address(account1), sessionKey1); + (spendLimitInfo,) = sessionKeyPlugin.getGasSpendLimit(address(account1), sessionKey1); assertTrue(spendLimitInfo.hasLimit); assertEq(spendLimitInfo.limit, 1 ether); @@ -412,7 +385,7 @@ contract SessionKeyGasLimitsTest is Test { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setGasSpendLimit, (1 ether, 1 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Use up 0.6 ether _runSessionKeyUserOp(100_000, 500_000, 1_000 gwei, 0.6 ether, sessionKey1Private, ""); @@ -432,8 +405,8 @@ contract SessionKeyGasLimitsTest is Test { entryPoint.handleOps(userOps, beneficiary); // Usage should still be at 0.6 - (ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo,) = - sessionKeyPermissionsPlugin.getGasSpendLimit(address(account1), sessionKey1); + (ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo,) = + sessionKeyPlugin.getGasSpendLimit(address(account1), sessionKey1); assertTrue(spendLimitInfo.hasLimit); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 0.6 ether); @@ -448,7 +421,7 @@ contract SessionKeyGasLimitsTest is Test { // Usage should now be at 0.4 (odd case, since the first one fits in the old interval, but the second one // doesn't. - (spendLimitInfo,) = sessionKeyPermissionsPlugin.getGasSpendLimit(address(account1), sessionKey1); + (spendLimitInfo,) = sessionKeyPlugin.getGasSpendLimit(address(account1), sessionKey1); assertTrue(spendLimitInfo.hasLimit); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 0.4 ether); @@ -466,7 +439,7 @@ contract SessionKeyGasLimitsTest is Test { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setGasSpendLimit, (1 ether, 1 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Use up 0.8 ether _runSessionKeyUserOp(100_000, 700_000, 1_000 gwei, 0.8 ether, sessionKey1Private, ""); @@ -489,8 +462,8 @@ contract SessionKeyGasLimitsTest is Test { entryPoint.handleOps(userOps, beneficiary); // Usage should still be at 0.8 - (ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo,) = - sessionKeyPermissionsPlugin.getGasSpendLimit(address(account1), sessionKey1); + (ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo,) = + sessionKeyPlugin.getGasSpendLimit(address(account1), sessionKey1); assertTrue(spendLimitInfo.hasLimit); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 0.8 ether); @@ -516,7 +489,7 @@ contract SessionKeyGasLimitsTest is Test { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setGasSpendLimit, (1 ether, 1 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Use up 0.6 ether _runSessionKeyUserOp(100_000, 500_000, 1_000 gwei, 0.6 ether, sessionKey1Private, ""); @@ -534,7 +507,7 @@ contract SessionKeyGasLimitsTest is Test { skip(1 days + 1 minutes); entryPoint.handleOps(userOps, beneficiary); - (, bool shouldReset) = sessionKeyPermissionsPlugin.getGasSpendLimit(address(account1), sessionKey1); + (, bool shouldReset) = sessionKeyPlugin.getGasSpendLimit(address(account1), sessionKey1); assertTrue(shouldReset, "Session key should report that it needs to be reset"); } @@ -550,8 +523,8 @@ contract SessionKeyGasLimitsTest is Test { _runSessionKeyUserOp(100_000, 300_000, 1_000 gwei, 0.4 ether, sessionKey1Private, ""); // The reset flag should now be false - (ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo, bool shouldReset) = - sessionKeyPermissionsPlugin.getGasSpendLimit(address(account1), sessionKey1); + (ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo, bool shouldReset) = + sessionKeyPlugin.getGasSpendLimit(address(account1), sessionKey1); assertFalse(shouldReset, "Session key should report that it does not need to be reset"); assertEq(spendLimitInfo.limitUsed, 1 ether); @@ -566,11 +539,11 @@ contract SessionKeyGasLimitsTest is Test { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setGasSpendLimit, (1 ether, 1 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // The reset flag should now be false - (ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo, bool shouldReset) = - sessionKeyPermissionsPlugin.getGasSpendLimit(address(account1), sessionKey1); + (ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo, bool shouldReset) = + sessionKeyPlugin.getGasSpendLimit(address(account1), sessionKey1); assertFalse(shouldReset, "Session key should report that it does not need to be reset"); assertEq(spendLimitInfo.limitUsed, 0.6 ether); @@ -582,11 +555,11 @@ contract SessionKeyGasLimitsTest is Test { test_sessionKeyGasLimits_refreshInterval_resetFlagTracking(); // Now, attempt to fix it by calling the public reset function - sessionKeyPermissionsPlugin.resetSessionKeyGasLimitTimestamp(address(account1), sessionKey1); + sessionKeyPlugin.resetSessionKeyGasLimitTimestamp(address(account1), sessionKey1); // The reset flag should now be false - (ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo, bool shouldReset) = - sessionKeyPermissionsPlugin.getGasSpendLimit(address(account1), sessionKey1); + (ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo, bool shouldReset) = + sessionKeyPlugin.getGasSpendLimit(address(account1), sessionKey1); assertFalse(shouldReset, "Session key should report that it does not need to be reset"); assertEq(spendLimitInfo.limitUsed, 0.6 ether); diff --git a/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol b/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol index 66986459..595cea56 100644 --- a/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol @@ -11,12 +11,8 @@ import {IMultiOwnerPlugin} from "../../../../src/plugins/owner/IMultiOwnerPlugin import {MultiOwnerPlugin} from "../../../../src/plugins/owner/MultiOwnerPlugin.sol"; import {ISessionKeyPlugin} from "../../../../src/plugins/session/ISessionKeyPlugin.sol"; import {SessionKeyPlugin} from "../../../../src/plugins/session/SessionKeyPlugin.sol"; -import {ISessionKeyPermissionsPlugin} from - "../../../../src/plugins/session/permissions/ISessionKeyPermissionsPlugin.sol"; import {ISessionKeyPermissionsUpdates} from "../../../../src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol"; -import {SessionKeyPermissionsPlugin} from - "../../../../src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol"; import {IEntryPoint} from "../../../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../../../src/interfaces/erc4337/UserOperation.sol"; import {IPluginManager} from "../../../../src/interfaces/IPluginManager.sol"; @@ -34,7 +30,6 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { MultiOwnerPlugin multiOwnerPlugin; MultiOwnerMSCAFactory factory; SessionKeyPlugin sessionKeyPlugin; - SessionKeyPermissionsPlugin sessionKeyPermissionsPlugin; address owner1; uint256 owner1Key; @@ -69,7 +64,6 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { ); sessionKeyPlugin = new SessionKeyPlugin(); - sessionKeyPermissionsPlugin = new SessionKeyPermissionsPlugin(); address[] memory owners1 = new address[](1); owners1[0] = owner1; @@ -93,40 +87,19 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { injectedHooks: new IPluginManager.InjectedHook[](0) }); - manifestHash = keccak256(abi.encode(sessionKeyPermissionsPlugin.pluginManifest())); - // Can reuse the same dependencies for this installation, because the requirements are the same. - vm.prank(owner1); - account1.installPlugin({ - plugin: address(sessionKeyPermissionsPlugin), - manifestHash: manifestHash, - pluginInitData: "", - dependencies: dependencies, - injectedHooks: new IPluginManager.InjectedHook[](0) - }); - // Create and add a session key (sessionKey1, sessionKey1Private) = makeAddrAndKey("sessionKey1"); - address[] memory sessionKeysToAdd = new address[](1); - sessionKeysToAdd[0] = sessionKey1; - - vm.prank(owner1); - SessionKeyPlugin(address(account1)).updateSessionKeys( - sessionKeysToAdd, new SessionKeyPlugin.SessionKeyToRemove[](0) - ); - - // Register the session key with the permissions plugin vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).registerKey(sessionKey1, 0); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); // Remove the allowlist bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall( - ISessionKeyPermissionsUpdates.setAccessListType, - (ISessionKeyPermissionsPlugin.ContractAccessControlType.NONE) + ISessionKeyPermissionsUpdates.setAccessListType, (ISessionKeyPlugin.ContractAccessControlType.NONE) ); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Create recipient addresses to receive ether recipient1 = makeAddr("recipient1"); @@ -136,13 +109,13 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { function test_sessionKeyNativeTokenSpendLimits_validateSetUp() public { // Check that the session key is registered - assertTrue(SessionKeyPlugin(address(account1)).isSessionKey(sessionKey1)); + assertTrue(sessionKeyPlugin.isSessionKeyOf(address(account1), sessionKey1)); // Check that the session key is registered with the permissions plugin and has its allowlist set up // correctly assertTrue( - sessionKeyPermissionsPlugin.getAccessControlType(address(account1), sessionKey1) - == ISessionKeyPermissionsPlugin.ContractAccessControlType.NONE + sessionKeyPlugin.getAccessControlType(address(account1), sessionKey1) + == ISessionKeyPlugin.ContractAccessControlType.NONE ); } @@ -152,8 +125,8 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { vm.warp(timestamp); // Assert that the limit starts out set, and at zero - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = - sessionKeyPermissionsPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo = + sessionKeyPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); assertTrue(spendLimitInfo.hasLimit); assertEq(spendLimitInfo.limit, 0); @@ -164,10 +137,10 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setNativeTokenSpendLimit, (limit, interval)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Verify the limit can be retrieved - spendLimitInfo = sessionKeyPermissionsPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); + spendLimitInfo = sessionKeyPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); if (limit == type(uint256).max) { // If the limit is "set" to this value, it is just removed. @@ -220,7 +193,7 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setNativeTokenSpendLimit, (1 ether, 0)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a user op that spends 1 wei, should succeed Call[] memory calls = new Call[](1); @@ -229,8 +202,8 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit is now updated - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = - sessionKeyPermissionsPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo = + sessionKeyPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 1 wei); @@ -254,7 +227,7 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setNativeTokenSpendLimit, (1 ether, 0)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a user op that spends 1 wei, should succeed Call[] memory calls = new Call[](1); @@ -282,7 +255,7 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setNativeTokenSpendLimit, (1 ether, 0)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Attempt to run a user op that spends > type(uint256).max (causing an overflow). Should fail. Call[] memory calls = new Call[](2); @@ -300,7 +273,7 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setNativeTokenSpendLimit, (1 ether, 0)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a user op that spends 1 wei, should succeed Call[] memory calls = new Call[](1); @@ -318,8 +291,8 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { ); // Assert that the limit is NOT updated - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = - sessionKeyPermissionsPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo = + sessionKeyPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); assertTrue(spendLimitInfo.hasLimit); assertEq(spendLimitInfo.limit, 1 ether); @@ -335,7 +308,7 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setNativeTokenSpendLimit, (1 ether, 0)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a multi execution user op spending 3 wei, should succeed Call[] memory calls = new Call[](3); @@ -346,8 +319,8 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit is now updated - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = - sessionKeyPermissionsPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo = + sessionKeyPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 3 wei); @@ -361,7 +334,7 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setNativeTokenSpendLimit, (1 ether, 0)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Attempt to run a multi execution user op spending 1.5 ether, should fail Call[] memory calls = new Call[](3); @@ -376,8 +349,8 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { ); // Assert that the limit is NOT updated - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = - sessionKeyPermissionsPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo = + sessionKeyPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); assertTrue(spendLimitInfo.hasLimit); assertEq(spendLimitInfo.limit, 1 ether); @@ -396,7 +369,7 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setNativeTokenSpendLimit, (1 ether, 1 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a user op that spends 1 wei, should succeed Call[] memory calls = new Call[](1); @@ -405,8 +378,8 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit is now updated and the last used timestamp is set. - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = - sessionKeyPermissionsPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo = + sessionKeyPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 1 wei); @@ -424,7 +397,7 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { ); // Assert that the limit is NOT updated - spendLimitInfo = sessionKeyPermissionsPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); + spendLimitInfo = sessionKeyPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); assertTrue(spendLimitInfo.hasLimit); assertEq(spendLimitInfo.limit, 1 ether); @@ -441,7 +414,7 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit is now updated and the last used timestamp is set. - spendLimitInfo = sessionKeyPermissionsPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); + spendLimitInfo = sessionKeyPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 1 ether); @@ -458,7 +431,7 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setNativeTokenSpendLimit, (1 ether, 1 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a user op that spends 1 wei, should succeed Call[] memory calls = new Call[](1); @@ -467,8 +440,8 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit is now updated and the last used timestamp is set. - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = - sessionKeyPermissionsPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo = + sessionKeyPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 1 wei); @@ -488,7 +461,7 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { ); // Assert that the limit is NOT updated - spendLimitInfo = sessionKeyPermissionsPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); + spendLimitInfo = sessionKeyPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); assertTrue(spendLimitInfo.hasLimit); assertEq(spendLimitInfo.limit, 1 ether); @@ -503,7 +476,7 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit is now updated and the last used timestamp is set. - spendLimitInfo = sessionKeyPermissionsPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); + spendLimitInfo = sessionKeyPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 1 ether); @@ -523,7 +496,7 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setNativeTokenSpendLimit, (1 ether, 1 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a user op that spends 1 wei, should succeed Call[] memory calls = new Call[](1); @@ -532,8 +505,8 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit is now updated and the last used timestamp is set. - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = - sessionKeyPermissionsPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo = + sessionKeyPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 1 wei); @@ -575,7 +548,7 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { bytes[] memory updates2 = new bytes[](1); updates2[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.updateTimeRange, (uint48(keyStartTime), 0)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates2); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates2); // Assert that the later start time is returned (key time range) vm.prank(address(entryPoint)); @@ -590,7 +563,7 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { updates2[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.updateTimeRange, (uint48(keyStartTime), 0)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates2); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates2); // Assert that the later start time is returned (spend limit) vm.prank(address(entryPoint)); @@ -611,7 +584,7 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setNativeTokenSpendLimit, (1 ether, 0 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Prepare a user op bundle that attempts to spend 1 ether twice. // The second call should revert because the first call will have updated the limit. @@ -671,8 +644,8 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { assertEq(recipient1.balance, 1 ether); // Assert that the spend limit is maxed out now. - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = - sessionKeyPermissionsPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo = + sessionKeyPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); assertEq(spendLimitInfo.limit, 1 ether); assertEq(spendLimitInfo.limitUsed, 1 ether); @@ -689,7 +662,7 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setNativeTokenSpendLimit, (1 ether, 1 days)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Run a user op that spends 1 wei, should succeed Call[] memory calls = new Call[](1); @@ -698,8 +671,8 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { _runSessionKeyUserOp(calls, sessionKey1Private, ""); // Assert that the limit is now updated and the last used timestamp is set. - ISessionKeyPermissionsPlugin.SpendLimitInfo memory spendLimitInfo = - sessionKeyPermissionsPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); + ISessionKeyPlugin.SpendLimitInfo memory spendLimitInfo = + sessionKeyPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); assertTrue(spendLimitInfo.hasLimit); assertEq(spendLimitInfo.limit, 1 ether); @@ -718,7 +691,7 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { ); // Assert that limits are NOT updated - spendLimitInfo = sessionKeyPermissionsPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); + spendLimitInfo = sessionKeyPlugin.getNativeTokenSpendLimitInfo(address(account1), sessionKey1); assertTrue(spendLimitInfo.hasLimit); assertEq(spendLimitInfo.limit, 1 ether); diff --git a/test/plugin/session/permissions/SessionKeyPermissionsPlugin.t.sol b/test/plugin/session/permissions/SessionKeyPermissions.t.sol similarity index 69% rename from test/plugin/session/permissions/SessionKeyPermissionsPlugin.t.sol rename to test/plugin/session/permissions/SessionKeyPermissions.t.sol index b1d82e9e..2b4cc14c 100644 --- a/test/plugin/session/permissions/SessionKeyPermissionsPlugin.t.sol +++ b/test/plugin/session/permissions/SessionKeyPermissions.t.sol @@ -11,12 +11,8 @@ import {IMultiOwnerPlugin} from "../../../../src/plugins/owner/IMultiOwnerPlugin import {MultiOwnerPlugin} from "../../../../src/plugins/owner/MultiOwnerPlugin.sol"; import {ISessionKeyPlugin} from "../../../../src/plugins/session/ISessionKeyPlugin.sol"; import {SessionKeyPlugin} from "../../../../src/plugins/session/SessionKeyPlugin.sol"; -import {ISessionKeyPermissionsPlugin} from - "../../../../src/plugins/session/permissions/ISessionKeyPermissionsPlugin.sol"; import {ISessionKeyPermissionsUpdates} from "../../../../src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol"; -import {SessionKeyPermissionsPlugin} from - "../../../../src/plugins/session/permissions/SessionKeyPermissionsPlugin.sol"; import {IEntryPoint} from "../../../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../../../src/interfaces/erc4337/UserOperation.sol"; import {IPluginManager} from "../../../../src/interfaces/IPluginManager.sol"; @@ -26,7 +22,7 @@ import {Call} from "../../../../src/interfaces/IStandardExecutor.sol"; import {Counter} from "../../../mocks/Counter.sol"; import {MultiOwnerMSCAFactory} from "../../../../src/factory/MultiOwnerMSCAFactory.sol"; -contract SessionKeyPermissionsPluginTest is Test { +contract SessionKeyPermissionsTest is Test { using ECDSA for bytes32; IEntryPoint entryPoint; @@ -34,7 +30,7 @@ contract SessionKeyPermissionsPluginTest is Test { MultiOwnerPlugin multiOwnerPlugin; MultiOwnerMSCAFactory factory; SessionKeyPlugin sessionKeyPlugin; - SessionKeyPermissionsPlugin sessionKeyPermissionsPlugin; + FunctionReference[] dependencies; address owner1; uint256 owner1Key; @@ -73,7 +69,6 @@ contract SessionKeyPermissionsPluginTest is Test { ); sessionKeyPlugin = new SessionKeyPlugin(); - sessionKeyPermissionsPlugin = new SessionKeyPermissionsPlugin(); address[] memory owners1 = new address[](1); owners1[0] = owner1; @@ -81,12 +76,15 @@ contract SessionKeyPermissionsPluginTest is Test { vm.deal(address(account1), 100 ether); bytes32 manifestHash = keccak256(abi.encode(sessionKeyPlugin.pluginManifest())); - FunctionReference[] memory dependencies = new FunctionReference[](2); - dependencies[0] = FunctionReferenceLib.pack( - address(multiOwnerPlugin), uint8(IMultiOwnerPlugin.FunctionId.USER_OP_VALIDATION_OWNER) + dependencies.push( + FunctionReferenceLib.pack( + address(multiOwnerPlugin), uint8(IMultiOwnerPlugin.FunctionId.USER_OP_VALIDATION_OWNER) + ) ); - dependencies[1] = FunctionReferenceLib.pack( - address(multiOwnerPlugin), uint8(IMultiOwnerPlugin.FunctionId.RUNTIME_VALIDATION_OWNER_OR_SELF) + dependencies.push( + FunctionReferenceLib.pack( + address(multiOwnerPlugin), uint8(IMultiOwnerPlugin.FunctionId.RUNTIME_VALIDATION_OWNER_OR_SELF) + ) ); vm.prank(owner1); account1.installPlugin({ @@ -97,34 +95,13 @@ contract SessionKeyPermissionsPluginTest is Test { injectedHooks: new IPluginManager.InjectedHook[](0) }); - manifestHash = keccak256(abi.encode(sessionKeyPermissionsPlugin.pluginManifest())); - // Can reuse the same dependencies for this installation, because the requirements are the same. - vm.prank(owner1); - account1.installPlugin({ - plugin: address(sessionKeyPermissionsPlugin), - manifestHash: manifestHash, - pluginInitData: "", - dependencies: dependencies, - injectedHooks: new IPluginManager.InjectedHook[](0) - }); - // Create and add a session key (sessionKey1, sessionKey1Private) = makeAddrAndKey("sessionKey1"); - address[] memory sessionKeysToAdd = new address[](1); - sessionKeysToAdd[0] = sessionKey1; - vm.prank(owner1); - SessionKeyPlugin(address(account1)).updateSessionKeys( - sessionKeysToAdd, new SessionKeyPlugin.SessionKeyToRemove[](0) - ); - - // Register the session key with the permissions plugin - vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).registerKey(sessionKey1, 0); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); // Initialize the interaction targets - counter1 = new Counter(); counter1.increment(); @@ -134,17 +111,9 @@ contract SessionKeyPermissionsPluginTest is Test { function test_sessionPerms_validateSetUp() public { assertEq( - uint8(sessionKeyPermissionsPlugin.getAccessControlType(address(account1), sessionKey1)), - uint8(ISessionKeyPermissionsPlugin.ContractAccessControlType.ALLOWLIST) - ); - } - - function test_sessionPerms_duplicateRegister() public { - vm.prank(owner1); - vm.expectRevert( - abi.encodeWithSelector(ISessionKeyPermissionsPlugin.KeyAlreadyRegistered.selector, sessionKey1) + uint8(sessionKeyPlugin.getAccessControlType(address(account1), sessionKey1)), + uint8(ISessionKeyPlugin.ContractAccessControlType.ALLOWLIST) ); - SessionKeyPermissionsPlugin(address(account1)).registerKey(sessionKey1, 0); } function test_sessionPerms_contractDefaultAllowList() public { @@ -163,11 +132,10 @@ contract SessionKeyPermissionsPluginTest is Test { // Remove the allowlist bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall( - ISessionKeyPermissionsUpdates.setAccessListType, - (ISessionKeyPermissionsPlugin.ContractAccessControlType.NONE) + ISessionKeyPermissionsUpdates.setAccessListType, (ISessionKeyPlugin.ContractAccessControlType.NONE) ); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Call should succeed after removing the allowlist _runSessionKeyExecUserOp( @@ -180,7 +148,7 @@ contract SessionKeyPermissionsPluginTest is Test { function test_sessionPerms_contractAllowList() public { // Assert the contracts to be added are not already on the allowlist (bool isOnList, bool checkSelectors) = - sessionKeyPermissionsPlugin.getAccessControlEntry(address(account1), sessionKey1, address(counter1)); + sessionKeyPlugin.getAccessControlEntry(address(account1), sessionKey1, address(counter1)); assertFalse(isOnList, "Address should not start on the list"); assertFalse(checkSelectors, "Address should not start with selectors checked"); @@ -191,11 +159,11 @@ contract SessionKeyPermissionsPluginTest is Test { ISessionKeyPermissionsUpdates.updateAccessListAddressEntry, (address(counter1), true, false) ); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Plugin should report the entry as on the list (isOnList, checkSelectors) = - sessionKeyPermissionsPlugin.getAccessControlEntry(address(account1), sessionKey1, address(counter1)); + sessionKeyPlugin.getAccessControlEntry(address(account1), sessionKey1, address(counter1)); assertTrue(isOnList); assertFalse(checkSelectors); @@ -223,14 +191,13 @@ contract SessionKeyPermissionsPluginTest is Test { // Add the denylist bytes[] memory updates = new bytes[](2); updates[0] = abi.encodeCall( - ISessionKeyPermissionsUpdates.setAccessListType, - (ISessionKeyPermissionsPlugin.ContractAccessControlType.DENYLIST) + ISessionKeyPermissionsUpdates.setAccessListType, (ISessionKeyPlugin.ContractAccessControlType.DENYLIST) ); updates[1] = abi.encodeCall( ISessionKeyPermissionsUpdates.updateAccessListAddressEntry, (address(counter1), true, false) ); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Check that the call should fail after adding the denylist _runSessionKeyExecUserOp( @@ -255,10 +222,10 @@ contract SessionKeyPermissionsPluginTest is Test { function test_sessionPerms_selectorAllowList() public { // Validate that the address and the selector do not start out enabled. (bool addressOnList, bool checkSelectors) = - sessionKeyPermissionsPlugin.getAccessControlEntry(address(account1), sessionKey1, address(counter1)); + sessionKeyPlugin.getAccessControlEntry(address(account1), sessionKey1, address(counter1)); assertFalse(addressOnList); assertFalse(checkSelectors); - bool selectorOnList = sessionKeyPermissionsPlugin.isSelectorOnAccessControlList( + bool selectorOnList = sessionKeyPlugin.isSelectorOnAccessControlList( address(account1), sessionKey1, address(counter1), Counter.increment.selector ); assertFalse(selectorOnList); @@ -274,14 +241,14 @@ contract SessionKeyPermissionsPluginTest is Test { ); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Validate that the address and the selector are now enabled (addressOnList, checkSelectors) = - sessionKeyPermissionsPlugin.getAccessControlEntry(address(account1), sessionKey1, address(counter1)); + sessionKeyPlugin.getAccessControlEntry(address(account1), sessionKey1, address(counter1)); assertTrue(addressOnList); assertTrue(checkSelectors); - selectorOnList = sessionKeyPermissionsPlugin.isSelectorOnAccessControlList( + selectorOnList = sessionKeyPlugin.isSelectorOnAccessControlList( address(account1), sessionKey1, address(counter1), Counter.increment.selector ); assertTrue(selectorOnList); @@ -310,8 +277,7 @@ contract SessionKeyPermissionsPluginTest is Test { // Add the denylist bytes[] memory updates = new bytes[](3); updates[0] = abi.encodeCall( - ISessionKeyPermissionsUpdates.setAccessListType, - (ISessionKeyPermissionsPlugin.ContractAccessControlType.DENYLIST) + ISessionKeyPermissionsUpdates.setAccessListType, (ISessionKeyPlugin.ContractAccessControlType.DENYLIST) ); updates[1] = abi.encodeCall( ISessionKeyPermissionsUpdates.updateAccessListAddressEntry, (address(counter1), true, true) @@ -322,7 +288,7 @@ contract SessionKeyPermissionsPluginTest is Test { ); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Call should fail after adding the denylist _runSessionKeyExecUserOp( @@ -349,7 +315,7 @@ contract SessionKeyPermissionsPluginTest is Test { updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.updateTimeRange, (startTime, endTime)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); Call[] memory calls = new Call[](1); calls[0] = Call({target: address(0), value: 0, data: ""}); @@ -387,11 +353,10 @@ contract SessionKeyPermissionsPluginTest is Test { // Remove the default allowlist bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall( - ISessionKeyPermissionsUpdates.setAccessListType, - (ISessionKeyPermissionsPlugin.ContractAccessControlType.NONE) + ISessionKeyPermissionsUpdates.setAccessListType, (ISessionKeyPlugin.ContractAccessControlType.NONE) ); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); (address sessionKey2, uint256 sessionKey2Private) = makeAddrAndKey("sessionKey2"); @@ -399,23 +364,20 @@ contract SessionKeyPermissionsPluginTest is Test { address[] memory keysToAdd = new address[](1); keysToAdd[0] = sessionKey2; - vm.prank(owner1); - SessionKeyPlugin(address(account1)).updateSessionKeys( - keysToAdd, new SessionKeyPlugin.SessionKeyToRemove[](0) + vm.startPrank(owner1); + SessionKeyPlugin(address(account1)).rotateSessionKey( + sessionKey1, sessionKeyPlugin.findPredecessor(address(account1), sessionKey1), sessionKey2 ); + vm.stopPrank(); - vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).rotateKey(sessionKey1, sessionKey2); - - // Attempting to use the previous key should fail + // Attempting to use the previous key should fail during the signature check _runSessionKeyExecUserOp( address(counter1), sessionKey1, sessionKey1Private, abi.encodeCall(Counter.increment, ()), 0 wei, - // Entrypoint wont recognize and bubble up KeyNotRegistered custom error in ver0.6.0 - abi.encodeWithSelector(IEntryPoint.FailedOp.selector, 0, "AA23 reverted (or OOG)") + abi.encodeWithSelector(IEntryPoint.FailedOp.selector, 0, "AA24 signature error") ); // Attempting to use the new key should succeed @@ -433,71 +395,43 @@ contract SessionKeyPermissionsPluginTest is Test { updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.updateTimeRange, (startTime, endTime)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Rotate the key address sessionKey2 = makeAddr("sessionKey2"); - vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).rotateKey(sessionKey1, sessionKey2); + vm.startPrank(owner1); + SessionKeyPlugin(address(account1)).rotateSessionKey( + sessionKey1, sessionKeyPlugin.findPredecessor(address(account1), sessionKey1), sessionKey2 + ); + vm.stopPrank(); // Check the rotated key's time range (uint48 returnedStartTime, uint48 returnedEndTime) = - sessionKeyPermissionsPlugin.getKeyTimeRange(address(account1), sessionKey2); + sessionKeyPlugin.getKeyTimeRange(address(account1), sessionKey2); assertEq(returnedStartTime, startTime); assertEq(returnedEndTime, endTime); } - function test_rotateKey_invalid_alreadyRegistered() public { - // Attempt to rotate the key to itself - vm.expectRevert( - abi.encodeWithSelector(ISessionKeyPermissionsPlugin.KeyAlreadyRegistered.selector, sessionKey1) - ); - vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).rotateKey(sessionKey1, sessionKey1); - - // Add a second session key - address sessionKey2 = makeAddr("sessionKey2"); - address[] memory keysToAdd = new address[](1); - keysToAdd[0] = sessionKey2; - vm.prank(owner1); - SessionKeyPlugin(address(account1)).updateSessionKeys( - keysToAdd, new SessionKeyPlugin.SessionKeyToRemove[](0) - ); - - // Register the second session key - vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).registerKey(sessionKey2, 0); - - // Attempt to rotate the key to the second session key - vm.expectRevert( - abi.encodeWithSelector(ISessionKeyPermissionsPlugin.KeyAlreadyRegistered.selector, sessionKey2) - ); - vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).rotateKey(sessionKey1, sessionKey2); - } - function testFuzz_sessionKeyPermissions_setRequiredPaymaster(address requiredPaymaster) public { bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setRequiredPaymaster, (requiredPaymaster)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Check the required paymaster - address returnedRequiredPaymaster = - sessionKeyPermissionsPlugin.getRequiredPaymaster(address(account1), sessionKey1); + address returnedRequiredPaymaster = sessionKeyPlugin.getRequiredPaymaster(address(account1), sessionKey1); assertEq(returnedRequiredPaymaster, requiredPaymaster); // Set the required paymaster to zero updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setRequiredPaymaster, (address(0))); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // Check the required paymaster - returnedRequiredPaymaster = - sessionKeyPermissionsPlugin.getRequiredPaymaster(address(account1), sessionKey1); + returnedRequiredPaymaster = sessionKeyPlugin.getRequiredPaymaster(address(account1), sessionKey1); assertEq(returnedRequiredPaymaster, address(0)); } @@ -508,13 +442,12 @@ contract SessionKeyPermissionsPluginTest is Test { // Disable the allowlist and disable native token spend checking. bytes[] memory updates = new bytes[](2); updates[0] = abi.encodeCall( - ISessionKeyPermissionsUpdates.setAccessListType, - (ISessionKeyPermissionsPlugin.ContractAccessControlType.NONE) + ISessionKeyPermissionsUpdates.setAccessListType, (ISessionKeyPlugin.ContractAccessControlType.NONE) ); updates[1] = abi.encodeCall(ISessionKeyPermissionsUpdates.setNativeTokenSpendLimit, (type(uint256).max, 0)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); vm.assume(providedPaymaster != address(0)); @@ -550,7 +483,7 @@ contract SessionKeyPermissionsPluginTest is Test { bytes[] memory updates2 = new bytes[](1); updates2[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setRequiredPaymaster, (requiredPaymaster)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates2); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates2); vm.prank(address(entryPoint)); validationData = account1.validateUserOp(userOp, userOpHash, 0); @@ -568,18 +501,17 @@ contract SessionKeyPermissionsPluginTest is Test { // Disable the allowlist bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall( - ISessionKeyPermissionsUpdates.setAccessListType, - (ISessionKeyPermissionsPlugin.ContractAccessControlType.NONE) + ISessionKeyPermissionsUpdates.setAccessListType, (ISessionKeyPlugin.ContractAccessControlType.NONE) ); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); // create a paymaster address that would match, if right-padded with zeroes address paymasterAddr = 0x1234123412341234000000000000000000000000; // Add it updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setRequiredPaymaster, (paymasterAddr)); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); Call[] memory calls = new Call[](1); calls[0] = Call({target: recipient, value: 1 wei, data: ""}); @@ -613,67 +545,101 @@ contract SessionKeyPermissionsPluginTest is Test { vm.startPrank(owner1); bytes[] memory updates = new bytes[](1); updates[0] = hex"112233"; // < 4 byte update - vm.expectRevert(abi.encodeWithSelector(ISessionKeyPermissionsPlugin.InvalidPermissionsUpdate.selector)); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + vm.expectRevert(abi.encodeWithSelector(ISessionKeyPlugin.InvalidPermissionsUpdate.selector)); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); updates[0] = hex"11223344"; // Invalid selector - vm.expectRevert(abi.encodeWithSelector(ISessionKeyPermissionsPlugin.InvalidPermissionsUpdate.selector)); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + vm.expectRevert(abi.encodeWithSelector(ISessionKeyPlugin.InvalidPermissionsUpdate.selector)); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); } function test_sessionKeyPerms_independentKeyStorage() public { address sessionKey2 = makeAddr("sessionKey2"); - address[] memory sessionKeysToAdd = new address[](1); - sessionKeysToAdd[0] = sessionKey2; - - vm.prank(owner1); - SessionKeyPlugin(address(account1)).updateSessionKeys( - sessionKeysToAdd, new SessionKeyPlugin.SessionKeyToRemove[](0) - ); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).registerKey(sessionKey2, 0); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey2, bytes32(0)); - ISessionKeyPermissionsPlugin.ContractAccessControlType accessControlType1; - ISessionKeyPermissionsPlugin.ContractAccessControlType accessControlType2; + ISessionKeyPlugin.ContractAccessControlType accessControlType1; + ISessionKeyPlugin.ContractAccessControlType accessControlType2; - accessControlType1 = sessionKeyPermissionsPlugin.getAccessControlType(address(account1), sessionKey1); - accessControlType2 = sessionKeyPermissionsPlugin.getAccessControlType(address(account1), sessionKey2); + accessControlType1 = sessionKeyPlugin.getAccessControlType(address(account1), sessionKey1); + accessControlType2 = sessionKeyPlugin.getAccessControlType(address(account1), sessionKey2); assertEq( uint8(accessControlType1), - uint8(ISessionKeyPermissionsPlugin.ContractAccessControlType.ALLOWLIST), + uint8(ISessionKeyPlugin.ContractAccessControlType.ALLOWLIST), "sessionKey1 should start with an allowlist" ); assertEq( uint8(accessControlType2), - uint8(ISessionKeyPermissionsPlugin.ContractAccessControlType.ALLOWLIST), + uint8(ISessionKeyPlugin.ContractAccessControlType.ALLOWLIST), "sessionKey2 should start with an allowlist" ); bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall( - ISessionKeyPermissionsUpdates.setAccessListType, - (ISessionKeyPermissionsPlugin.ContractAccessControlType.NONE) + ISessionKeyPermissionsUpdates.setAccessListType, (ISessionKeyPlugin.ContractAccessControlType.NONE) ); vm.prank(owner1); - SessionKeyPermissionsPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); - accessControlType1 = sessionKeyPermissionsPlugin.getAccessControlType(address(account1), sessionKey1); - accessControlType2 = sessionKeyPermissionsPlugin.getAccessControlType(address(account1), sessionKey2); + accessControlType1 = sessionKeyPlugin.getAccessControlType(address(account1), sessionKey1); + accessControlType2 = sessionKeyPlugin.getAccessControlType(address(account1), sessionKey2); assertEq( uint8(accessControlType1), - uint8(ISessionKeyPermissionsPlugin.ContractAccessControlType.NONE), + uint8(ISessionKeyPlugin.ContractAccessControlType.NONE), "sessionKey1 should now have no allowlist" ); assertEq( uint8(accessControlType2), - uint8(ISessionKeyPermissionsPlugin.ContractAccessControlType.ALLOWLIST), + uint8(ISessionKeyPlugin.ContractAccessControlType.ALLOWLIST), "sessionKey2 should still have an allowlist" ); } + function test_sessionKeyPerms_reinstallResets() public { + // Tests that reinstalling the plugin resets the permissions. + + uint48 time1 = uint48(2000); + uint48 time2 = uint48(3000); + // Set the time range on the key + bytes[] memory updates = new bytes[](1); + updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.updateTimeRange, (time1, time2)); + vm.prank(owner1); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + + // Assert that the time range is set + (uint48 returnedStartTime, uint48 returnedEndTime) = + sessionKeyPlugin.getKeyTimeRange(address(account1), sessionKey1); + assertEq(returnedStartTime, time1); + assertEq(returnedEndTime, time2); + + // Uninstall the session key plugin + vm.prank(owner1); + account1.uninstallPlugin(address(sessionKeyPlugin), "", "", new bytes[](0)); + + // Reinstall the session key plugin. + vm.startPrank(owner1); + account1.installPlugin({ + plugin: address(sessionKeyPlugin), + manifestHash: keccak256(abi.encode(sessionKeyPlugin.pluginManifest())), + pluginInitData: abi.encode(new address[](0)), + dependencies: dependencies, + injectedHooks: new IPluginManager.InjectedHook[](0) + }); + vm.stopPrank(); + + // Re-add the session key + vm.prank(owner1); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); + + // Assert that the time range is reset + (returnedStartTime, returnedEndTime) = sessionKeyPlugin.getKeyTimeRange(address(account1), sessionKey1); + assertEq(returnedStartTime, uint48(0)); + assertEq(returnedEndTime, uint48(0)); + } + function _runSessionKeyExecUserOp( address target, address sessionKey, From 2b6d7f66fd6d84ecf2c8893bc27eefb520ea60e1 Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Fri, 19 Jan 2024 14:30:34 -0800 Subject: [PATCH 061/106] fix: [spearbit-39] Declare memory-safe assembly block in AccountStorage (#84) --- src/libraries/AccountStorageV1.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/AccountStorageV1.sol b/src/libraries/AccountStorageV1.sol index cded2f8c..c419b7bb 100644 --- a/src/libraries/AccountStorageV1.sol +++ b/src/libraries/AccountStorageV1.sol @@ -114,7 +114,7 @@ contract AccountStorageV1 { bytes32 internal constant _V1_STORAGE_SLOT = 0xade46bbfcf6f898a43d541e42556d456ca0bf9b326df8debc0f29d3f811a0300; function _getAccountStorage() internal pure returns (AccountStorage storage storage_) { - assembly { + assembly ("memory-safe") { storage_.slot := _V1_STORAGE_SLOT } } From e707f52bf642aa1684898700f8678de5f1713c4c Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Fri, 19 Jan 2024 14:31:55 -0800 Subject: [PATCH 062/106] fix: [spearbit-37] Add event emission to MultiOwnerPlugin install/uninstall (#85) --- src/plugins/owner/MultiOwnerPlugin.sol | 4 ++++ test/plugin/owner/MultiOwnerPlugin.t.sol | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/plugins/owner/MultiOwnerPlugin.sol b/src/plugins/owner/MultiOwnerPlugin.sol index 0a28eaaa..90ffc101 100644 --- a/src/plugins/owner/MultiOwnerPlugin.sol +++ b/src/plugins/owner/MultiOwnerPlugin.sol @@ -189,10 +189,14 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { revert EmptyOwnersNotAllowed(); } _addOwnersOrRevert(_owners, msg.sender, initialOwners); + + emit OwnerUpdated(msg.sender, initialOwners, new address[](0)); } /// @inheritdoc BasePlugin function onUninstall(bytes calldata) external override { + address[] memory ownersToRemove = ownersOf(msg.sender); + emit OwnerUpdated(msg.sender, new address[](0), ownersToRemove); _owners.clear(msg.sender); } diff --git a/test/plugin/owner/MultiOwnerPlugin.t.sol b/test/plugin/owner/MultiOwnerPlugin.t.sol index 4f71a141..52308f1e 100644 --- a/test/plugin/owner/MultiOwnerPlugin.t.sol +++ b/test/plugin/owner/MultiOwnerPlugin.t.sol @@ -34,6 +34,9 @@ contract MultiOwnerPluginTest is Test { ContractOwner public contractOwner; address[] public ownerArray; + // Re-declare events for vm.expectEmit + event OwnerUpdated(address indexed account, address[] addedOwners, address[] removedOwners); + function setUp() public { plugin = new MultiOwnerPlugin(); entryPoint = IEntryPoint(address(new EntryPoint())); @@ -52,6 +55,8 @@ contract MultiOwnerPluginTest is Test { ownerArray[1] = owner3; ownerArray[2] = owner1; + vm.expectEmit(true, true, true, true); + emit OwnerUpdated(accountA, ownerArray, new address[](0)); vm.startPrank(accountA); plugin.onInstall(abi.encode(ownerArray)); } @@ -67,6 +72,11 @@ contract MultiOwnerPluginTest is Test { } function test_onUninstall_success() public { + // Populate the expected event using `plugin.ownersOf` instead of `ownerArray` to reverse the order of + // owners. + vm.expectEmit(true, true, true, true); + emit OwnerUpdated(accountA, new address[](0), plugin.ownersOf(accountA)); + plugin.onUninstall(abi.encode("")); address[] memory returnedOwners = plugin.ownersOf(accountA); assertEq(0, returnedOwners.length); @@ -136,6 +146,9 @@ contract MultiOwnerPluginTest is Test { ownersToRemove[0] = owner1; ownersToRemove[1] = owner2; + vm.expectEmit(true, true, true, true); + emit OwnerUpdated(accountA, new address[](0), ownersToRemove); + plugin.updateOwners(new address[](0), ownersToRemove); (address[] memory newOwnerList) = plugin.ownersOf(accountA); From ba1ce7c761c71b608576bf5c83d48bb25991cb17 Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Fri, 19 Jan 2024 14:32:18 -0800 Subject: [PATCH 063/106] fix: [spearbit-28] Simplify storage access syntax of executeFromPluginExternal (#87) --- src/account/UpgradeableModularAccount.sol | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index ec7fc1d4..ed71a4f7 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -292,12 +292,15 @@ contract UpgradeableModularAccount is // // We allow calls where the data may be less than 4 bytes - it's up to the calling contract to // determine how to handle this. + + PermittedExternalCallData storage permittedExternalCallData = + storage_.permittedExternalCalls[IPlugin(callingPlugin)][target]; + bool isTargetCallPermitted; - if (storage_.permittedExternalCalls[IPlugin(callingPlugin)][target].addressPermitted) { + if (permittedExternalCallData.addressPermitted) { isTargetCallPermitted = ( - storage_.permittedExternalCalls[IPlugin(callingPlugin)][target].anySelectorPermitted - || data.length == 0 - || storage_.permittedExternalCalls[IPlugin(callingPlugin)][target].permittedSelectors[bytes4(data)] + permittedExternalCallData.anySelectorPermitted || data.length == 0 + || permittedExternalCallData.permittedSelectors[bytes4(data)] ); } else { isTargetCallPermitted = storage_.pluginData[callingPlugin].anyExternalAddressPermitted; From bf932c9f203e2b3f3ab6cf699fb3f88fe02487d5 Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Fri, 19 Jan 2024 15:00:54 -0800 Subject: [PATCH 064/106] fix: [spearbit-100] Limit what selectors can be used by session keys on ERC-20 tokens with a spending limit (#66) --- .../permissions/SessionKeyPermissions.sol | 11 +++++++++- .../SessionKeyERC20SpendLimits.t.sol | 22 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/plugins/session/permissions/SessionKeyPermissions.sol b/src/plugins/session/permissions/SessionKeyPermissions.sol index 4b8202ce..128023ba 100644 --- a/src/plugins/session/permissions/SessionKeyPermissions.sol +++ b/src/plugins/session/permissions/SessionKeyPermissions.sol @@ -169,6 +169,11 @@ abstract contract SessionKeyPermissions is ISessionKeyPlugin, SessionKeyPermissi FunctionData storage functionData = _functionDataOf(msg.sender, keyId, target, selector); validationSuccess = !functionData.isOnList; } + + // Check the selector in use if the target is a known ERC-20 contract with a spending limit. + if (contractData.isERC20WithSpendLimit && !isAllowedERC20Function(selector)) { + validationSuccess = false; + } } /// @dev Runs during execution to re-check and update the spend limits of the session key in use. @@ -424,7 +429,7 @@ abstract contract SessionKeyPermissions is ISessionKeyPlugin, SessionKeyPermissi // trailing zero bytes. bytes4 selector = bytes4(callData); - if (selector == IERC20.transfer.selector || selector == IERC20.approve.selector) { + if (isAllowedERC20Function(selector)) { // Expected length: 68 bytes (4 selector + 32 address + 32 amount) if (callData.length < 68) { return 0; @@ -609,4 +614,8 @@ abstract contract SessionKeyPermissions is ISessionKeyPlugin, SessionKeyPermissi function _max(uint48 a, uint48 b) internal pure returns (uint48) { return a > b ? a : b; } + + function isAllowedERC20Function(bytes4 selector) internal pure returns (bool) { + return selector == IERC20.transfer.selector || selector == IERC20.approve.selector; + } } diff --git a/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol b/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol index a34470c3..951d7124 100644 --- a/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol @@ -186,6 +186,28 @@ contract SessionKeyERC20SpendLimitsTest is Test { SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); } + function test_sessionKeyERC20SpendLimits_unknownSelectorFails() public { + // Give the account a starting balance + token1.mint(address(account1), 100 ether); + + // Set the limit to 1 ether + bytes[] memory updates = new bytes[](1); + updates[0] = + abi.encodeCall(ISessionKeyPermissionsUpdates.setERC20SpendLimit, (address(token1), 1 ether, 0 days)); + vm.prank(owner1); + SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); + + // Run a user op that tries to call name(), a non-allowed selector. + Call[] memory calls = new Call[](1); + calls[0] = Call({target: address(token1), data: abi.encodeCall(token1.name, ()), value: 0}); + + _runSessionKeyUserOp( + calls, + sessionKey1Private, + abi.encodeWithSelector(IEntryPoint.FailedOp.selector, 0, "AA24 signature error") + ); + } + function test_sessionKeyERC20SpendLimits_enforceLimit_none_basic() public { // Give the account a starting balance token1.mint(address(account1), 100 ether); From 61baa5e6297dcd360a8384258b47201386de9375 Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Fri, 19 Jan 2024 15:01:15 -0800 Subject: [PATCH 065/106] fix: [spearbit-85] Add documentation for no ERC-20 spend limit checks during user op validation (#79) --- src/plugins/session/permissions/SessionKeyPermissions.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/session/permissions/SessionKeyPermissions.sol b/src/plugins/session/permissions/SessionKeyPermissions.sol index 128023ba..bb30d4da 100644 --- a/src/plugins/session/permissions/SessionKeyPermissions.sol +++ b/src/plugins/session/permissions/SessionKeyPermissions.sol @@ -50,7 +50,8 @@ abstract contract SessionKeyPermissions is ISessionKeyPlugin, SessionKeyPermissi // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ /// @dev A check to run during user op validation that checks the permissions of the session key used to - /// validate the user op. + /// validate the user op. Note that this function does not check ERC-20 spend limits, which are checked + /// during the execution phase. function _checkUserOpPermissions(UserOperation calldata userOp, Call[] memory calls, address sessionKey) internal returns (uint256) From 27af95a1c8833b827dd249e987bf53a47998aa67 Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Fri, 19 Jan 2024 15:02:05 -0800 Subject: [PATCH 066/106] fix: [quanstamp-req] Contract data key (#81) --- src/plugins/session/permissions/SessionKeyPermissionsBase.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/session/permissions/SessionKeyPermissionsBase.sol b/src/plugins/session/permissions/SessionKeyPermissionsBase.sol index e4594221..01a9dca7 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsBase.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsBase.sol @@ -80,7 +80,7 @@ abstract contract SessionKeyPermissionsBase is ISessionKeyPlugin { // ContractData (128 bytes) // 12 padding zeros || associated address || CONTRACT_DATA_PREFIX || batch index || sessionKeyId - // || contractAddress || 12 padding zero bytes + // || 12 padding zero bytes || contractAddress // FunctionData (128 bytes) // 12 padding zeros || associated address || FUNCTION_DATA_PREFIX || batch index || sessionKeyId || selector @@ -154,7 +154,7 @@ abstract contract SessionKeyPermissionsBase is ISessionKeyPlugin { PluginStorageLib.allocateAssociatedStorageKey(associated, prefixAndBatchIndex, 2); bytes32 contractDataKey1 = SessionKeyId.unwrap(id); - bytes32 contractDataKey2 = bytes32(bytes20(contractAddress)); + bytes32 contractDataKey2 = bytes32(uint256(uint160(contractAddress))); return _toContractData( PluginStorageLib.associatedStorageLookup(associatedStorageKey, contractDataKey1, contractDataKey2) ); From 47fb48b50075878c51c4f5b9f504449825f0da77 Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Fri, 19 Jan 2024 15:04:42 -0800 Subject: [PATCH 067/106] fix: [spearbit-71] Expanded comment documentation for structs in SessionKeyPermissionsBase (#86) --- .../permissions/SessionKeyPermissionsBase.sol | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/plugins/session/permissions/SessionKeyPermissionsBase.sol b/src/plugins/session/permissions/SessionKeyPermissionsBase.sol index 01a9dca7..baf8d7d7 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsBase.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsBase.sol @@ -10,47 +10,59 @@ abstract contract SessionKeyPermissionsBase is ISessionKeyPlugin { // Implementation-internal structs not exposed by the external interface. + // Holds permission data unique to each session key on an account. struct SessionKeyData { - // Contract access control type + // Contract access control type. ContractAccessControlType contractAccessControlType; // Key time range: limits when a key may be used. uint48 validAfter; uint48 validUntil; + // Boolean flags for optional rules. bool hasRequiredPaymaster; bool hasGasLimit; + bool nativeTokenSpendLimitBypassed; // By default, spend limits are enforced and the limit is zero. + // Flag for resetting gas limit last used timestamp during the execution phase. bool gasLimitResetThisBundle; - // Native token spend limits - bool nativeTokenSpendLimitBypassed; // By default, spend limits ARE enforced and the limit is zero. + // Time info for gas and native token spend limits. SpendLimitTimeInfo gasLimitTimeInfo; SpendLimitTimeInfo nativeTokenSpendLimitTimeInfo; // Required paymaster rule address requiredPaymaster; + // Limit amounts and limit usages for gas and native token spend limits. SpendLimit gasLimit; SpendLimit nativeTokenSpendLimit; } - /// @dev These structs are not held in an Associated Enumerable set, so the elements must be emitted from - /// events to use offchain. + // Holds permission data for an address associated with a session key and an account. struct ContractData { + // Whether or not this address is on the access control list. bool isOnList; + // Whether or not to check selectors for this address, per the current access control type's rules. bool checkSelectors; + // Whether or not this address is a known ERC-20 contract with a spend limit. bool isERC20WithSpendLimit; + // Spend limit configuration data for ERC-20 contracts. SpendLimitTimeInfo erc20SpendLimitTimeInfo; SpendLimit erc20SpendLimit; } + // Holds permission data for a function selector associated with a session key, an account, + // and a contract address. struct FunctionData { + // Whether or not this selector is on the access control list. bool isOnList; } // Spending limit info structs. // Split into two structs to allow custom storage arrangements. + // Holds time info for spend limits. struct SpendLimitTimeInfo { uint48 lastUsed; uint48 refreshInterval; } + // Holds spend limit data. struct SpendLimit { uint256 limitAmount; uint256 limitUsed; From ead7836286ea04bcadb63096f44f0c56e50542cd Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Fri, 19 Jan 2024 15:05:22 -0800 Subject: [PATCH 068/106] fix: [spearbit-26] Upgrade to solc 0.8.22 and remove explicit unchecked blocks for loop increments (#88) --- foundry.toml | 10 +- src/account/AccountLoupe.sol | 15 +- src/account/PluginManagerInternals.sol | 166 +++--------------- src/account/UpgradeableModularAccount.sol | 44 +---- src/helpers/FactoryHelpers.sol | 6 +- src/libraries/AssociatedLinkedListSetLib.sol | 6 +- src/libraries/CastLib.sol | 6 +- src/plugins/owner/MultiOwnerPlugin.sol | 18 +- src/plugins/session/SessionKeyPlugin.sol | 24 +-- .../permissions/SessionKeyPermissions.sol | 18 +- 10 files changed, 56 insertions(+), 257 deletions(-) diff --git a/foundry.toml b/foundry.toml index eb276488..843500cd 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,5 +1,5 @@ [profile.default] -solc = '0.8.21' +solc = '0.8.22' evm_version='paris' via_ir = true src = 'src' @@ -14,20 +14,16 @@ ignored_error_codes = [] runs = 500 [invariant] -runs=500 +runs = 500 fail_on_revert = true depth = 10 [profile.optimized-build] -via_ir = true -evm_version='paris' script = 'src' test = 'src' out = 'out-optimized' [profile.lite] -solc = '0.8.21' -evm_version='paris' via_ir = false optimizer = true optimizer_runs = 10_000 @@ -35,12 +31,10 @@ ignored_error_codes = [] [profile.deep.fuzz] runs = 10000 -evm_version='paris' [profile.deep.invariant] runs = 5000 depth = 32 -evm_version='paris' [fmt] line_length = 115 diff --git a/src/account/AccountLoupe.sol b/src/account/AccountLoupe.sol index a1992ecc..e5f69f39 100644 --- a/src/account/AccountLoupe.sol +++ b/src/account/AccountLoupe.sol @@ -85,10 +85,9 @@ abstract contract AccountLoupe is IAccountLoupe, AccountStorageV1 { uint256 maxExecHooksLength = postOnlyExecHooksLength; // There can only be as many associated post hooks to run as there are pre hooks. - for (uint256 i = 0; i < preExecHooksLength;) { + for (uint256 i = 0; i < preExecHooksLength; ++i) { unchecked { maxExecHooksLength += storedHooks.preHooks.getCount(CastLib.toSetValue(preExecHooks[i])); - ++i; } } @@ -96,19 +95,18 @@ abstract contract AccountLoupe is IAccountLoupe, AccountStorageV1 { execHooks = new ExecutionHooks[](maxExecHooksLength); uint256 actualExecHooksLength = 0; - for (uint256 i = 0; i < preExecHooksLength;) { + for (uint256 i = 0; i < preExecHooksLength; ++i) { FunctionReference[] memory associatedPostExecHooks = CastLib.toFunctionReferenceArray(storedHooks.associatedPostHooks[preExecHooks[i]].getAll()); uint256 associatedPostExecHooksLength = associatedPostExecHooks.length; if (associatedPostExecHooksLength > 0) { - for (uint256 j = 0; j < associatedPostExecHooksLength;) { + for (uint256 j = 0; j < associatedPostExecHooksLength; ++j) { execHooks[actualExecHooksLength].preExecHook = preExecHooks[i]; execHooks[actualExecHooksLength].postExecHook = associatedPostExecHooks[j]; unchecked { ++actualExecHooksLength; - ++j; } } } else { @@ -118,18 +116,13 @@ abstract contract AccountLoupe is IAccountLoupe, AccountStorageV1 { ++actualExecHooksLength; } } - - unchecked { - ++i; - } } - for (uint256 i = 0; i < postOnlyExecHooksLength;) { + for (uint256 i = 0; i < postOnlyExecHooksLength; ++i) { execHooks[actualExecHooksLength].postExecHook = postOnlyExecHooks[i]; unchecked { ++actualExecHooksLength; - ++i; } } diff --git a/src/account/PluginManagerInternals.sol b/src/account/PluginManagerInternals.sol index a4791da9..50ccaf9b 100644 --- a/src/account/PluginManagerInternals.sol +++ b/src/account/PluginManagerInternals.sol @@ -355,7 +355,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { revert InvalidDependenciesProvided(); } - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { // Check the dependency interface id over the address of the dependency. (address dependencyAddr,) = dependencies[i].unpack(); @@ -371,32 +371,20 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { // Increment the dependency's dependents counter. storage_.pluginData[dependencyAddr].dependentCount += 1; - - unchecked { - ++i; - } } // Update components according to the manifest. // Install execution functions length = manifest.executionFunctions.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { _setExecutionFunction(manifest.executionFunctions[i], plugin); - - unchecked { - ++i; - } } // Add installed plugin and selectors this plugin can call length = manifest.permittedExecutionSelectors.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { _enableExecFromPlugin(manifest.permittedExecutionSelectors[i], plugin, storage_); - - unchecked { - ++i; - } } // Add the permitted external calls to the account. @@ -405,7 +393,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { } else { // Only store the specific permitted external calls if "permit any" flag was not set. length = manifest.permittedExternalCalls.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { ManifestExternalCallPermission memory externalCallPermission = manifest.permittedExternalCalls[i]; PermittedExternalCallData storage permittedExternalCallData = @@ -417,18 +405,10 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { permittedExternalCallData.anySelectorPermitted = true; } else { uint256 externalContractSelectorsLength = externalCallPermission.selectors.length; - for (uint256 j = 0; j < externalContractSelectorsLength;) { + for (uint256 j = 0; j < externalContractSelectorsLength; ++j) { permittedExternalCallData.permittedSelectors[externalCallPermission.selectors[j]] = true; - - unchecked { - ++j; - } } } - - unchecked { - ++i; - } } } @@ -439,7 +419,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { assembly ("memory-safe") { sstore(injectedHooksArray.slot, length) } - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { InjectedHook memory hook = injectedHooks[i]; // Check that the dependency is installed. This also blocks self-dependencies. @@ -466,15 +446,11 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { ? FunctionReferenceLib.pack(hook.providingPlugin, hook.injectedHooksInfo.postExecHookFunctionId) : FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE ); - - unchecked { - ++i; - } } // Add user operation validation functions length = manifest.userOpValidationFunctions.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { ManifestAssociatedFunction memory mv = manifest.userOpValidationFunctions[i]; _addUserOpValidationFunction( mv.executionSelector, @@ -482,15 +458,11 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { mv.associatedFunction, plugin, dependencies, ManifestAssociatedFunctionType.NONE ) ); - - unchecked { - ++i; - } } // Add runtime validation functions length = manifest.runtimeValidationFunctions.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { ManifestAssociatedFunction memory mv = manifest.runtimeValidationFunctions[i]; _addRuntimeValidationFunction( mv.executionSelector, @@ -501,15 +473,11 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { ManifestAssociatedFunctionType.RUNTIME_VALIDATION_ALWAYS_ALLOW ) ); - - unchecked { - ++i; - } } // Add pre user operation validation hooks length = manifest.preUserOpValidationHooks.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { ManifestAssociatedFunction memory mh = manifest.preUserOpValidationHooks[i]; _addPreUserOpValidationHook( mh.executionSelector, @@ -520,15 +488,11 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY ) ); - - unchecked { - ++i; - } } // Add pre runtime validation hooks length = manifest.preRuntimeValidationHooks.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { ManifestAssociatedFunction memory mh = manifest.preRuntimeValidationHooks[i]; _addPreRuntimeValidationHook( mh.executionSelector, @@ -539,14 +503,11 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY ) ); - unchecked { - ++i; - } } // Add pre and post execution hooks length = manifest.executionHooks.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { ManifestExecutionHook memory mh = manifest.executionHooks[i]; _addExecHooks( mh.executionSelector, @@ -557,15 +518,11 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { mh.postExecHook, plugin, dependencies, ManifestAssociatedFunctionType.NONE ) ); - - unchecked { - ++i; - } } // Add pre and post permitted call hooks length = manifest.permittedCallHooks.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { _addPermittedCallHooks( manifest.permittedCallHooks[i].executionSelector, plugin, @@ -582,23 +539,16 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { ManifestAssociatedFunctionType.NONE ) ); - - unchecked { - ++i; - } } // Add new interface ids the plugin enabled for the account length = manifest.interfaceIds.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { bytes4 interfaceId = manifest.interfaceIds[i]; if (interfaceId == type(IPlugin).interfaceId) { revert IPluginInterfaceNotAllowed(); } storage_.supportedInterfaces[interfaceId] += 1; - unchecked { - ++i; - } } // Add the plugin metadata to the account @@ -612,7 +562,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { // Call injected hooks' onHookApply after all setup, this is before calling plugin onInstall length = injectedHooks.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { InjectedHook memory hook = injectedHooks[i]; /* solhint-disable no-empty-blocks */ @@ -625,10 +575,6 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { // zero out hookApplyData to reduce log cost injectedHooks[i].hookApplyData = new bytes(0); - - unchecked { - ++i; - } } // Initialize the plugin storage for the account. @@ -668,16 +614,12 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { // Remove this plugin as a dependent from its dependencies. FunctionReference[] memory dependencies = pluginData.dependencies; uint256 length = dependencies.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { FunctionReference dependency = dependencies[i]; (address dependencyAddr,) = dependency.unpack(); // Decrement the dependent count for the dependency function. storage_.pluginData[dependencyAddr].dependentCount -= 1; - - unchecked { - ++i; - } } // Remove the plugin metadata from the account. @@ -687,7 +629,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { // Remove pre and post permitted call hooks length = args.manifest.permittedCallHooks.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { _removePermittedCallHooks( args.manifest.permittedCallHooks[i].executionSelector, args.plugin, @@ -704,15 +646,11 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { ManifestAssociatedFunctionType.NONE ) ); - - unchecked { - ++i; - } } // Remove pre and post execution function hooks length = args.manifest.executionHooks.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { ManifestExecutionHook memory mh = args.manifest.executionHooks[i]; _removeExecHooks( mh.executionSelector, @@ -723,15 +661,11 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { mh.postExecHook, args.plugin, dependencies, ManifestAssociatedFunctionType.NONE ) ); - - unchecked { - ++i; - } } // Remove pre runtime validation function hooks length = args.manifest.preRuntimeValidationHooks.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { ManifestAssociatedFunction memory mh = args.manifest.preRuntimeValidationHooks[i]; _removePreRuntimeValidationHook( @@ -743,15 +677,11 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY ) ); - - unchecked { - ++i; - } } // Remove pre user op validation function hooks length = args.manifest.preUserOpValidationHooks.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { ManifestAssociatedFunction memory mh = args.manifest.preUserOpValidationHooks[i]; _removePreUserOpValidationHook( @@ -763,34 +693,22 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY ) ); - - unchecked { - ++i; - } } // Remove runtime validation function hooks length = args.manifest.runtimeValidationFunctions.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { bytes4 executionSelector = args.manifest.runtimeValidationFunctions[i].executionSelector; storage_.selectorData[executionSelector].runtimeValidation = FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE; - - unchecked { - ++i; - } } // Remove user op validation function hooks length = args.manifest.userOpValidationFunctions.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { bytes4 executionSelector = args.manifest.userOpValidationFunctions[i].executionSelector; storage_.selectorData[executionSelector].userOpValidation = FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE; - - unchecked { - ++i; - } } // Remove permitted external call permissions, anyExternalAddressPermitted is cleared when pluginData being @@ -798,7 +716,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { if (!args.manifest.permitAnyExternalAddress) { // Only clear the specific permitted external calls if "permit any" flag was not set. length = args.manifest.permittedExternalCalls.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { ManifestExternalCallPermission memory externalCallPermission = args.manifest.permittedExternalCalls[i]; @@ -812,24 +730,16 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { permittedExternalCallData.anySelectorPermitted = false; } else { uint256 externalContractSelectorsLength = externalCallPermission.selectors.length; - for (uint256 j = 0; j < externalContractSelectorsLength;) { + for (uint256 j = 0; j < externalContractSelectorsLength; ++j) { permittedExternalCallData.permittedSelectors[externalCallPermission.selectors[j]] = false; - - unchecked { - ++j; - } } } - - unchecked { - ++i; - } } } // Remove injected hooks length = pluginData.injectedHooks.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { StoredInjectedHook memory hook = pluginData.injectedHooks[i]; // Decrement the dependent count for the plugin providing the hook. @@ -843,41 +753,25 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { ? FunctionReferenceLib.pack(hook.providingPlugin, hook.postExecHookFunctionId) : FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE ); - - unchecked { - ++i; - } } // Remove permitted account execution function call permissions length = args.manifest.permittedExecutionSelectors.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { storage_.permittedCalls[_getPermittedCallKey(args.plugin, args.manifest.permittedExecutionSelectors[i])] .callPermitted = false; - - unchecked { - ++i; - } } // Remove installed execution function length = args.manifest.executionFunctions.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { storage_.selectorData[args.manifest.executionFunctions[i]].plugin = address(0); - - unchecked { - ++i; - } } // Decrease supported interface ids' counters length = args.manifest.interfaceIds.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { storage_.supportedInterfaces[args.manifest.interfaceIds[i]] -= 1; - - unchecked { - ++i; - } } // Call onHookUnapply on all injected hooks @@ -887,7 +781,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { if (hasUnapplyHookData && hookUnapplyData.length != length) { revert ArrayLengthMismatch(); } - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { StoredInjectedHook memory hook = pluginData.injectedHooks[i]; /* solhint-disable no-empty-blocks */ @@ -907,10 +801,6 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { emit PluginIgnoredHookUnapplyCallbackFailure(args.plugin, hook.providingPlugin); } /* solhint-enable no-empty-blocks */ - - unchecked { - ++i; - } } // Clear the plugin storage for the account. diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index ed71a4f7..aa931973 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -109,14 +109,10 @@ contract UpgradeableModularAccount is FunctionReference[] memory emptyDependencies = new FunctionReference[](0); InjectedHook[] memory emptyInjectedHooks = new InjectedHook[](0); - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { _installPlugin( plugins[i], manifestHashes[i], pluginInstallDatas[i], emptyDependencies, emptyInjectedHooks ); - - unchecked { - ++i; - } } emit ModularAccountInitialized(_ENTRY_POINT); @@ -210,12 +206,8 @@ contract UpgradeableModularAccount is uint256 callsLength = calls.length; results = new bytes[](callsLength); - for (uint256 i = 0; i < callsLength;) { + for (uint256 i = 0; i < callsLength; ++i) { results[i] = _exec(calls[i].target, calls[i].value, calls[i].data); - - unchecked { - ++i; - } } _postNativeFunction(postExecHooks, postHookArgs); @@ -456,7 +448,7 @@ contract UpgradeableModularAccount is ); preUserOpValidationHooksLength = preUserOpValidationHooks.length; - for (uint256 i = 0; i < preUserOpValidationHooksLength;) { + for (uint256 i = 0; i < preUserOpValidationHooksLength; ++i) { // FunctionReference preUserOpValidationHook = preUserOpValidationHooks[i]; if (preUserOpValidationHooks[i].isEmptyOrMagicValue()) { @@ -477,10 +469,6 @@ contract UpgradeableModularAccount is revert UnexpectedAggregator(plugin, functionId, address(uint160(currentValidationData))); } validationData = _coalescePreValidation(validationData, currentValidationData); - - unchecked { - ++i; - } } } @@ -521,7 +509,7 @@ contract UpgradeableModularAccount is ); uint256 preRuntimeValidationHooksLength = preRuntimeValidationHooks.length; - for (uint256 i = 0; i < preRuntimeValidationHooksLength;) { + for (uint256 i = 0; i < preRuntimeValidationHooksLength; ++i) { FunctionReference preRuntimeValidationHook = preRuntimeValidationHooks[i]; if (preRuntimeValidationHook.isEmptyOrMagicValue()) { @@ -535,10 +523,6 @@ contract UpgradeableModularAccount is _updatePluginCallBufferFunctionId(callBuffer, functionId); _executeRuntimePluginFunction(callBuffer, plugin, PreRuntimeValidationHookFailed.selector); - - unchecked { - ++i; - } } } @@ -742,7 +726,7 @@ contract UpgradeableModularAccount is } _updatePluginCallBufferSelector(callBuffer, IPlugin.preExecutionHook.selector); - for (uint256 i = 0; i < preExecHooksLength;) { + for (uint256 i = 0; i < preExecHooksLength; ++i) { FunctionReference preExecHook = preHooks[i]; if (preExecHook.isEmptyOrMagicValue()) { @@ -766,10 +750,6 @@ contract UpgradeableModularAccount is if (postHooks[adjustedIndex].length > 0) { hookReturnData[adjustedIndex] = abi.decode(_collectReturnData(), (bytes)); } - - unchecked { - ++i; - } } } @@ -790,7 +770,7 @@ contract UpgradeableModularAccount is // We don't need to run each associated post-hook in reverse order, because the associativity we want // to maintain is reverse order of associated pre-hooks. uint256 postHooksToRunLength = postHooksToRun.length; - for (uint256 j = 0; j < postHooksToRunLength;) { + for (uint256 j = 0; j < postHooksToRunLength; ++j) { (address plugin, uint8 functionId) = postHooksToRun[j].unpack(); // Execute the post hook with the current post hook args @@ -799,12 +779,10 @@ contract UpgradeableModularAccount is catch (bytes memory revertReason) { revert PostExecHookReverted(plugin, functionId, revertReason); } - - unchecked { - ++j; - } } + // Solidity v0.8.22 allows the optimizer to automatically remove checking on for loop increments, but + // not decrements. Therefore we need to use unchecked here to avoid the extra costs for checked math. unchecked { --i; } @@ -826,7 +804,7 @@ contract UpgradeableModularAccount is uint256 startingIndex ) internal view { uint256 preExecHooksLength = preExecHooks.length; - for (uint256 i = 0; i < preExecHooksLength;) { + for (uint256 i = 0; i < preExecHooksLength; ++i) { FunctionReference preExecHook = preExecHooks[i]; // If the pre-exec hook has associated post hooks, cache them in the postHooks array. @@ -837,10 +815,6 @@ contract UpgradeableModularAccount is } // In no-associated-post-hooks case, we're OK returning the default value, which is an array of length // 0. - - unchecked { - ++i; - } } } diff --git a/src/helpers/FactoryHelpers.sol b/src/helpers/FactoryHelpers.sol index ce6f4ed2..baabe829 100644 --- a/src/helpers/FactoryHelpers.sol +++ b/src/helpers/FactoryHelpers.sol @@ -7,15 +7,11 @@ library FactoryHelpers { /// @return bool if the owners array is valid function isValidOwnerArray(address[] calldata owners) internal pure returns (bool) { address currentOwnerValue; - for (uint256 i = 0; i < owners.length;) { + for (uint256 i = 0; i < owners.length; ++i) { if (owners[i] <= currentOwnerValue) { return false; } currentOwnerValue = owners[i]; - - unchecked { - ++i; - } } return true; } diff --git a/src/libraries/AssociatedLinkedListSetLib.sol b/src/libraries/AssociatedLinkedListSetLib.sol index 2e008e23..b12c9b15 100644 --- a/src/libraries/AssociatedLinkedListSetLib.sol +++ b/src/libraries/AssociatedLinkedListSetLib.sol @@ -382,16 +382,12 @@ library AssociatedLinkedListSetLib { keyBuffer = _allocateTempKeyBuffer(set, associated); cursor = SENTINEL_VALUE; - for (uint256 i = 0; i < count;) { + for (uint256 i = 0; i < count; ++i) { StoragePointer cursorSlot = _mapLookup(keyBuffer, cursor); bytes32 cursorValue = _load(cursorSlot); bytes32 cleared = clearFlags(cursorValue); res[i] = SetValue.wrap(bytes30(cleared)); cursor = cleared; - - unchecked { - ++i; - } } } diff --git a/src/libraries/CastLib.sol b/src/libraries/CastLib.sol index e5a0b8fc..5d9a8374 100644 --- a/src/libraries/CastLib.sol +++ b/src/libraries/CastLib.sol @@ -26,12 +26,8 @@ library CastLib { } uint256 length = values.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { valuesBytes[i] >>= 96; - - unchecked { - i++; - } } assembly ("memory-safe") { diff --git a/src/plugins/owner/MultiOwnerPlugin.sol b/src/plugins/owner/MultiOwnerPlugin.sol index 90ffc101..a3333a9c 100644 --- a/src/plugins/owner/MultiOwnerPlugin.sol +++ b/src/plugins/owner/MultiOwnerPlugin.sol @@ -377,15 +377,11 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { address[] memory ownersToAdd ) private { uint256 length = ownersToAdd.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { // Catches address(0), duplicated addresses if (!ownerSet.tryAdd(associated, CastLib.toSetValue(ownersToAdd[i]))) { revert InvalidOwner(ownersToAdd[i]); } - - unchecked { - ++i; - } } } @@ -395,14 +391,10 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { address[] memory ownersToRemove ) private { uint256 length = ownersToRemove.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { if (!ownerSet.tryRemove(associated, CastLib.toSetValue(ownersToRemove[i]))) { revert OwnerDoesNotExist(ownersToRemove[i]); } - - unchecked { - ++i; - } } } @@ -413,14 +405,10 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { { address[] memory owners_ = ownersOf(associated); uint256 length = owners_.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { if (SignatureChecker.isValidERC1271SignatureNow(owners_[i], digest, signature)) { return true; } - - unchecked { - ++i; - } } return false; } diff --git a/src/plugins/session/SessionKeyPlugin.sol b/src/plugins/session/SessionKeyPlugin.sol index 17135b40..4d5b0d69 100644 --- a/src/plugins/session/SessionKeyPlugin.sol +++ b/src/plugins/session/SessionKeyPlugin.sol @@ -86,14 +86,10 @@ contract SessionKeyPlugin is ISessionKeyPlugin, SessionKeyPermissions, BasePlugi // Unset the key id for all session keys. address[] memory sessionKeys = CastLib.toAddressArray(_sessionKeys.getAll(msg.sender)); uint256 length = sessionKeys.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { _updateSessionKeyId(msg.sender, sessionKeys[i], SessionKeyId.wrap(bytes32(0))); emit SessionKeyRemoved(msg.sender, sessionKeys[i]); - - unchecked { - ++i; - } } _sessionKeys.clear(msg.sender); @@ -116,14 +112,10 @@ contract SessionKeyPlugin is ISessionKeyPlugin, SessionKeyPermissions, BasePlugi uint256 callsLength = calls.length; bytes[] memory results = new bytes[](callsLength); - for (uint256 i = 0; i < callsLength;) { + for (uint256 i = 0; i < callsLength; ++i) { Call calldata call = calls[i]; results[i] = IPluginExecutor(msg.sender).executeFromPluginExternal(call.target, call.value, call.data); - - unchecked { - ++i; - } } return results; @@ -214,16 +206,12 @@ contract SessionKeyPlugin is ISessionKeyPlugin, SessionKeyPermissions, BasePlugi uint256 length = sessionKeys.length; bytes32 predecessor = SENTINEL_VALUE; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { if (sessionKeys[i] == sessionKey) { return predecessor; } predecessor = bytes32(bytes20(sessionKeys[i])); - - unchecked { - ++i; - } } revert SessionKeyNotFound(sessionKey); @@ -372,14 +360,10 @@ contract SessionKeyPlugin is ISessionKeyPlugin, SessionKeyPermissions, BasePlugi address[] memory sessionKeysToAdd = abi.decode(data, (address[])); uint256 length = sessionKeysToAdd.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { // Use the public function to add the session key, set the key id, and emit the event. // Note that no tags are set when adding keys with this method. addSessionKey(sessionKeysToAdd[i], bytes32(0)); - - unchecked { - ++i; - } } } diff --git a/src/plugins/session/permissions/SessionKeyPermissions.sol b/src/plugins/session/permissions/SessionKeyPermissions.sol index bb30d4da..f2390ba4 100644 --- a/src/plugins/session/permissions/SessionKeyPermissions.sol +++ b/src/plugins/session/permissions/SessionKeyPermissions.sol @@ -25,12 +25,8 @@ abstract contract SessionKeyPermissions is ISessionKeyPlugin, SessionKeyPermissi (SessionKeyData storage sessionKeyData, SessionKeyId keyId) = _loadSessionKey(msg.sender, sessionKey); uint256 length = updates.length; - for (uint256 i = 0; i < length;) { + for (uint256 i = 0; i < length; ++i) { _performSessionKeyPermissionsUpdate(keyId, sessionKeyData, updates[i]); - - unchecked { - ++i; - } } emit PermissionsUpdated(msg.sender, sessionKey, updates); @@ -72,15 +68,11 @@ abstract contract SessionKeyPermissions is ISessionKeyPlugin, SessionKeyPermissi bool validationSuccess = callsLength > 0; { ContractAccessControlType accessControlType = sessionKeyData.contractAccessControlType; - for (uint256 i = 0; i < callsLength;) { + for (uint256 i = 0; i < callsLength; ++i) { Call memory call = calls[i]; nativeTokenSpend += call.value; validationSuccess = validationSuccess && _checkCallPermissions(accessControlType, keyId, call.target, call.data); - - unchecked { - ++i; - } } } @@ -188,7 +180,7 @@ abstract contract SessionKeyPermissions is ISessionKeyPlugin, SessionKeyPermissi SessionKeyId keyId = _sessionKeyIdOf(msg.sender, sessionKey); SessionKeyData storage sessionKeyData = _sessionKeyDataOf(msg.sender, keyId); - for (uint256 i = 0; i < callsLength;) { + for (uint256 i = 0; i < callsLength; ++i) { Call memory call = calls[i]; newNativeTokenUsage += call.value; @@ -206,10 +198,6 @@ abstract contract SessionKeyPermissions is ISessionKeyPlugin, SessionKeyPermissi revert ERC20SpendLimitExceeded(msg.sender, sessionKey, call.target); } } - - unchecked { - ++i; - } } if (!sessionKeyData.nativeTokenSpendLimitBypassed) { From cff1b59567714f6111ac562a4e46525ce797fd55 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Sat, 20 Jan 2024 17:05:00 -0500 Subject: [PATCH 069/106] fix: [spearbit-97] cap owners on creation to 100 (#82) --- src/factory/MultiOwnerMSCAFactory.sol | 14 +++++-- .../MultiOwnerTokenReceiverMSCAFactory.sol | 14 +++++-- test/factory/MultiOwnerMSCAFactoryTest.t.sol | 40 ++++++++++++++++++ .../MultiOwnerTokenReceiverFactoryTest.t.sol | 41 +++++++++++++++++++ 4 files changed, 103 insertions(+), 6 deletions(-) diff --git a/src/factory/MultiOwnerMSCAFactory.sol b/src/factory/MultiOwnerMSCAFactory.sol index 116de48d..698a4228 100644 --- a/src/factory/MultiOwnerMSCAFactory.sol +++ b/src/factory/MultiOwnerMSCAFactory.sol @@ -17,13 +17,15 @@ import {FactoryHelpers} from "../helpers/FactoryHelpers.sol"; /// @dev There is a reliance on the assumption that the plugin manifest will remain static, following ERC-6900. If /// this assumption is broken then account deployments would be bricked. contract MultiOwnerMSCAFactory is Ownable2Step { + IEntryPoint public immutable ENTRYPOINT; address public immutable MULTI_OWNER_PLUGIN; address public immutable IMPL; bytes32 internal immutable _MULTI_OWNER_PLUGIN_MANIFEST_HASH; - IEntryPoint public immutable ENTRYPOINT; + uint256 internal constant _MAX_OWNERS_ON_CREATION = 100; - error OwnersArrayEmpty(); error InvalidOwner(); + error OwnersArrayEmpty(); + error OwnersLimitExceeded(); error TransferFailed(); /// @notice Constructor for the factory @@ -120,11 +122,17 @@ contract MultiOwnerMSCAFactory is Ownable2Step { /// @param owners array of addresses of the owner /// @return address of counterfactual account function getAddress(uint256 salt, address[] calldata owners) external view returns (address) { - // array can't be empty + // Array can't be empty. if (owners.length == 0) { revert OwnersArrayEmpty(); } + // This protects against counterfactuals being generated against an exceptionally large number of owners + // that may exceed the block gas limit when actually creating the account. + if (owners.length > _MAX_OWNERS_ON_CREATION) { + revert OwnersLimitExceeded(); + } + if (!FactoryHelpers.isValidOwnerArray(owners)) { revert InvalidOwner(); } diff --git a/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol b/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol index 9313f4c1..1a15847b 100644 --- a/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol +++ b/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol @@ -17,15 +17,17 @@ import {FactoryHelpers} from "../helpers/FactoryHelpers.sol"; /// @dev There is a reliance on the assumption that the plugin manifest will remain static, following ERC-6900. If /// this assumption is broken then account deployments would be bricked. contract MultiOwnerTokenReceiverMSCAFactory is Ownable2Step { + IEntryPoint public immutable ENTRYPOINT; address public immutable MULTI_OWNER_PLUGIN; address public immutable TOKEN_RECEIVER_PLUGIN; address public immutable IMPL; bytes32 internal immutable _MULTI_OWNER_PLUGIN_MANIFEST_HASH; bytes32 internal immutable _TOKEN_RECEIVER_PLUGIN_MANIFEST_HASH; - IEntryPoint public immutable ENTRYPOINT; + uint256 internal constant _MAX_OWNERS_ON_CREATION = 100; - error OwnersArrayEmpty(); error InvalidOwner(); + error OwnersArrayEmpty(); + error OwnersLimitExceeded(); error TransferFailed(); /// @notice Constructor for the factory @@ -128,11 +130,17 @@ contract MultiOwnerTokenReceiverMSCAFactory is Ownable2Step { /// @param owners array of addresses of the owner /// @return address of counterfactual account function getAddress(uint256 salt, address[] calldata owners) external view returns (address) { - // array can't be empty + // Array can't be empty. if (owners.length == 0) { revert OwnersArrayEmpty(); } + // This protects against counterfactuals being generated against an exceptionally large number of owners + // that may exceed the block gas limit when actually creating the account. + if (owners.length > _MAX_OWNERS_ON_CREATION) { + revert OwnersLimitExceeded(); + } + if (!FactoryHelpers.isValidOwnerArray(owners)) { revert InvalidOwner(); } diff --git a/test/factory/MultiOwnerMSCAFactoryTest.t.sol b/test/factory/MultiOwnerMSCAFactoryTest.t.sol index 6fa78928..d60b22d1 100644 --- a/test/factory/MultiOwnerMSCAFactoryTest.t.sol +++ b/test/factory/MultiOwnerMSCAFactoryTest.t.sol @@ -25,9 +25,11 @@ contract MultiOwnerMSCAFactoryTest is Test { address public badImpl = address(4); address[] public owners; + address[] public largeOwners; bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + uint256 internal constant _MAX_OWNERS_ON_CREATION = 100; function setUp() public { owners.push(owner1); @@ -39,6 +41,9 @@ contract MultiOwnerMSCAFactoryTest is Test { factory = new MultiOwnerMSCAFactory( address(this), address(multiOwnerPlugin), impl, manifestHash, IEntryPoint(address(entryPoint)) ); + for (uint160 i = 0; i < _MAX_OWNERS_ON_CREATION; i++) { + largeOwners.push(address(i + 1)); + } vm.deal(address(this), 100 ether); } @@ -139,6 +144,41 @@ contract MultiOwnerMSCAFactoryTest is Test { assertEq(factory.owner(), owner1); } + function test_getAddressWithMaxOwnersAndDeploy() public { + address addr = factory.getAddress(0, largeOwners); + assertEq(addr, factory.createAccount(0, largeOwners)); + } + + function test_getAddressWithTooManyOwners() public { + largeOwners.push(address(101)); + vm.expectRevert(MultiOwnerMSCAFactory.OwnersLimitExceeded.selector); + factory.getAddress(0, largeOwners); + } + + function test_getAddressWithUnsortedOwners() public { + address[] memory tempOwners = new address[](2); + tempOwners[0] = address(2); + tempOwners[1] = address(1); + vm.expectRevert(MultiOwnerMSCAFactory.InvalidOwner.selector); + factory.getAddress(0, tempOwners); + } + + function test_deployWithDuplicateOwners() public { + address[] memory tempOwners = new address[](2); + tempOwners[0] = address(1); + tempOwners[1] = address(1); + vm.expectRevert(MultiOwnerMSCAFactory.InvalidOwner.selector); + factory.createAccount(0, tempOwners); + } + + function test_deployWithUnsortedOwners() public { + address[] memory tempOwners = new address[](2); + tempOwners[0] = address(2); + tempOwners[1] = address(1); + vm.expectRevert(MultiOwnerMSCAFactory.InvalidOwner.selector); + factory.createAccount(0, tempOwners); + } + // to receive funds from withdraw receive() external payable {} } diff --git a/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol b/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol index a42c4343..f5dd68d4 100644 --- a/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol +++ b/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol @@ -33,6 +33,7 @@ contract MultiOwnerTokenReceiverMSCAFactoryTest is Test { address public nftHolder = address(3); address[] public owners; + address[] public largeOwners; uint256[] public tokenIds; uint256[] public tokenAmts; uint256[] public zeroTokenAmts; @@ -40,6 +41,7 @@ contract MultiOwnerTokenReceiverMSCAFactoryTest is Test { uint256 internal constant _TOKEN_AMOUNT = 1 ether; uint256 internal constant _TOKEN_ID = 0; uint256 internal constant _BATCH_TOKEN_IDS = 5; + uint256 internal constant _MAX_OWNERS_ON_CREATION = 100; bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; @@ -78,6 +80,10 @@ contract MultiOwnerTokenReceiverMSCAFactoryTest is Test { tokenAmts.push(_TOKEN_AMOUNT); zeroTokenAmts.push(0); } + + for (uint160 i = 0; i < _MAX_OWNERS_ON_CREATION; i++) { + largeOwners.push(address(i + 1)); + } } function test_addressMatch() public { @@ -195,6 +201,41 @@ contract MultiOwnerTokenReceiverMSCAFactoryTest is Test { assertEq(factory.owner(), owner1); } + function test_getAddressWithMaxOwnersAndDeploy() public { + address addr = factory.getAddress(0, largeOwners); + assertEq(addr, factory.createAccount(0, largeOwners)); + } + + function test_getAddressWithTooManyOwners() public { + largeOwners.push(address(101)); + vm.expectRevert(MultiOwnerTokenReceiverMSCAFactory.OwnersLimitExceeded.selector); + factory.getAddress(0, largeOwners); + } + + function test_getAddressWithUnsortedOwners() public { + address[] memory tempOwners = new address[](2); + tempOwners[0] = address(2); + tempOwners[1] = address(1); + vm.expectRevert(MultiOwnerTokenReceiverMSCAFactory.InvalidOwner.selector); + factory.getAddress(0, tempOwners); + } + + function test_deployWithDuplicateOwners() public { + address[] memory tempOwners = new address[](2); + tempOwners[0] = address(1); + tempOwners[1] = address(1); + vm.expectRevert(MultiOwnerTokenReceiverMSCAFactory.InvalidOwner.selector); + factory.createAccount(0, tempOwners); + } + + function test_deployWithUnsortedOwners() public { + address[] memory tempOwners = new address[](2); + tempOwners[0] = address(2); + tempOwners[1] = address(1); + vm.expectRevert(MultiOwnerTokenReceiverMSCAFactory.InvalidOwner.selector); + factory.createAccount(0, tempOwners); + } + // to receive funds from withdraw receive() external payable {} } From 11cb21e221df206b33a2a4bc0718b32c3ad3492c Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Sat, 20 Jan 2024 18:47:06 -0500 Subject: [PATCH 070/106] fix: [spearbit-35] flip condition to remove a NOT (#63) --- src/plugins/session/permissions/SessionKeyPermissions.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/session/permissions/SessionKeyPermissions.sol b/src/plugins/session/permissions/SessionKeyPermissions.sol index f2390ba4..071db835 100644 --- a/src/plugins/session/permissions/SessionKeyPermissions.sol +++ b/src/plugins/session/permissions/SessionKeyPermissions.sol @@ -128,7 +128,7 @@ abstract contract SessionKeyPermissions is ISessionKeyPlugin, SessionKeyPermissi // otherwise a packed struct of the aggregator address (0 here), and two // 6-byte timestamps indicating the start and end times at which the op // is valid. - return uint160(!validationSuccess ? 1 : 0) | (uint256(validUntil) << 160) + return uint160(validationSuccess ? 0 : 1) | (uint256(validUntil) << 160) | (uint256(currentValidAfter) << (208)); } From f874acdef44d5a88870fe0b4ee15400df56b3500 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Mon, 22 Jan 2024 10:59:38 -0500 Subject: [PATCH 071/106] chore: bump to pragma ^0.8.22 (#89) --- .vscode/settings.json | 2 +- script/Deploy.s.sol | 2 +- src/account/AccountExecutor.sol | 2 +- src/account/AccountLoupe.sol | 2 +- src/account/AccountStorageInitializable.sol | 2 +- src/account/PluginManagerInternals.sol | 2 +- src/account/UpgradeableModularAccount.sol | 2 +- src/factory/MultiOwnerMSCAFactory.sol | 2 +- src/factory/MultiOwnerTokenReceiverMSCAFactory.sol | 2 +- src/helpers/FactoryHelpers.sol | 2 +- src/helpers/KnownSelectors.sol | 2 +- src/helpers/ValidationDataHelpers.sol | 2 +- src/interfaces/IAccountInitializable.sol | 2 +- src/interfaces/IAccountLoupe.sol | 2 +- src/interfaces/IAccountView.sol | 2 +- src/interfaces/IPlugin.sol | 2 +- src/interfaces/IPluginExecutor.sol | 2 +- src/interfaces/IPluginManager.sol | 2 +- src/interfaces/IStandardExecutor.sol | 2 +- src/interfaces/erc4337/IAccount.sol | 2 +- src/interfaces/erc4337/IAggregator.sol | 2 +- src/interfaces/erc4337/IEntryPoint.sol | 2 +- src/interfaces/erc4337/IPaymaster.sol | 2 +- src/interfaces/erc4337/UserOperation.sol | 2 +- src/libraries/AccountStorageV1.sol | 2 +- src/libraries/AssociatedLinkedListSetLib.sol | 2 +- src/libraries/CastLib.sol | 2 +- src/libraries/CountableLinkedListSetLib.sol | 2 +- src/libraries/FunctionReferenceLib.sol | 2 +- src/libraries/LinkedListSetLib.sol | 2 +- src/libraries/LinkedListSetUtils.sol | 2 +- src/libraries/PluginStorageLib.sol | 2 +- src/plugins/BasePlugin.sol | 2 +- src/plugins/TokenReceiverPlugin.sol | 2 +- src/plugins/owner/IMultiOwnerPlugin.sol | 2 +- src/plugins/owner/MultiOwnerPlugin.sol | 2 +- src/plugins/session/ISessionKeyPlugin.sol | 2 +- src/plugins/session/SessionKeyPlugin.sol | 2 +- .../session/permissions/ISessionKeyPermissionsUpdates.sol | 2 +- src/plugins/session/permissions/SessionKeyPermissions.sol | 2 +- src/plugins/session/permissions/SessionKeyPermissionsBase.sol | 2 +- src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol | 2 +- test/TestUtils.sol | 2 +- test/Utils.sol | 2 +- test/account/AccountExecHooks.t.sol | 2 +- test/account/AccountLoupe.t.sol | 2 +- test/account/AccountPermittedCallHooks.t.sol | 2 +- test/account/AccountPreValidationHooks.t.sol | 2 +- test/account/AccountReturnData.t.sol | 2 +- test/account/ExecuteFromPluginPermissions.t.sol | 2 +- test/account/ManifestValidity.t.sol | 2 +- test/account/UpgradeableModularAccount.t.sol | 2 +- test/account/UpgradeableModularAccountPluginManager.t.sol | 2 +- test/account/ValidationIntersection.t.sol | 2 +- test/account/phases/AccountStatePhases.t.sol | 2 +- test/account/phases/AccountStatePhasesExec.t.sol | 2 +- test/account/phases/AccountStatePhasesRTValidation.t.sol | 2 +- test/account/phases/AccountStatePhasesUOValidation.t.sol | 2 +- test/comparison/CompareSimpleAccount.t.sol | 2 +- test/factory/MultiOwnerMSCAFactoryTest.t.sol | 2 +- test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol | 2 +- test/helpers/KnownSelectors.t.sol | 2 +- test/invariant/AssociatedLinkedListSetLibInvariants.t.sol | 2 +- test/invariant/LLSLRepro.t.sol | 2 +- test/invariant/LinkedListSetLibInvariants.t.sol | 2 +- test/invariant/handlers/AssociatedLinkedListSetHandler.sol | 2 +- test/invariant/handlers/LinkedListSetHandler.sol | 2 +- test/libraries/AccountStorage.t.sol | 2 +- test/libraries/AssociatedLinkedListSetLib.t.sol | 2 +- test/libraries/CountableLinkedListSetLib.t.sol | 2 +- test/libraries/FunctionReferenceLib.t.sol | 2 +- test/libraries/LinkedListSetLib.t.sol | 2 +- test/libraries/PluginStorageLib.t.sol | 2 +- test/mocks/ContractOwner.sol | 2 +- test/mocks/Counter.sol | 2 +- test/mocks/Counter.t.sol | 2 +- test/mocks/MockDiamondStorageContract.sol | 2 +- test/mocks/MockPlugin.sol | 2 +- test/mocks/plugins/AccountStateMutatingPlugin.sol | 2 +- test/mocks/plugins/BadTransferOwnershipPlugin.sol | 2 +- test/mocks/plugins/BaseTestPlugin.sol | 2 +- test/mocks/plugins/ChangingManifestPlugin.sol | 2 +- test/mocks/plugins/ComprehensivePlugin.sol | 2 +- test/mocks/plugins/ExecFromPluginPermissionsMocks.sol | 2 +- test/mocks/plugins/ManifestValidityMocks.sol | 2 +- test/mocks/plugins/ReturnDataPluginMocks.sol | 2 +- test/mocks/plugins/UninstallErrorsPlugin.sol | 2 +- test/mocks/plugins/ValidationPluginMocks.sol | 2 +- test/mocks/tokens/MockERC1155.sol | 2 +- test/mocks/tokens/MockERC20.sol | 2 +- test/mocks/tokens/MockERC777.sol | 2 +- test/plugin/TokenReceiverPlugin.t.sol | 2 +- test/plugin/owner/MultiOwnerPlugin.t.sol | 2 +- test/plugin/owner/MultiOwnerPluginIntegration.t.sol | 2 +- test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol | 2 +- .../plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol | 2 +- test/plugin/session/permissions/SessionKeyGasLimits.t.sol | 2 +- .../session/permissions/SessionKeyNativeTokenSpendLimits.t.sol | 2 +- test/plugin/session/permissions/SessionKeyPermissions.t.sol | 2 +- test/upgrade/LightAccountToMSCA.t.sol | 2 +- test/upgrade/MSCAToMSCA.t.sol | 2 +- 101 files changed, 101 insertions(+), 101 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 284161a3..7ac44da1 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.22", "editor.formatOnSave": true, "[solidity]": { "editor.defaultFormatter": "JuanBlanco.solidity" diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index e80a797a..939f3643 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {console} from "forge-std/Test.sol"; import {Script} from "forge-std/Script.sol"; diff --git a/src/account/AccountExecutor.sol b/src/account/AccountExecutor.sol index fce39689..76644353 100644 --- a/src/account/AccountExecutor.sol +++ b/src/account/AccountExecutor.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; diff --git a/src/account/AccountLoupe.sol b/src/account/AccountLoupe.sol index e5f69f39..c9c87c4d 100644 --- a/src/account/AccountLoupe.sol +++ b/src/account/AccountLoupe.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {KnownSelectors} from "../helpers/KnownSelectors.sol"; diff --git a/src/account/AccountStorageInitializable.sol b/src/account/AccountStorageInitializable.sol index fd66b269..f57d3a8a 100644 --- a/src/account/AccountStorageInitializable.sol +++ b/src/account/AccountStorageInitializable.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; diff --git a/src/account/PluginManagerInternals.sol b/src/account/PluginManagerInternals.sol index 50ccaf9b..45147b5a 100644 --- a/src/account/PluginManagerInternals.sol +++ b/src/account/PluginManagerInternals.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index aa931973..23746605 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; diff --git a/src/factory/MultiOwnerMSCAFactory.sol b/src/factory/MultiOwnerMSCAFactory.sol index 698a4228..71318905 100644 --- a/src/factory/MultiOwnerMSCAFactory.sol +++ b/src/factory/MultiOwnerMSCAFactory.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; diff --git a/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol b/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol index 1a15847b..4940fbbd 100644 --- a/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol +++ b/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; diff --git a/src/helpers/FactoryHelpers.sol b/src/helpers/FactoryHelpers.sol index baabe829..7f53870c 100644 --- a/src/helpers/FactoryHelpers.sol +++ b/src/helpers/FactoryHelpers.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; library FactoryHelpers { /// @dev The owner array must be in strictly ascending order and not include the 0 address. diff --git a/src/helpers/KnownSelectors.sol b/src/helpers/KnownSelectors.sol index bb573c7a..fbc6032d 100644 --- a/src/helpers/KnownSelectors.sol +++ b/src/helpers/KnownSelectors.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {UUPSUpgradeable} from "../../ext/UUPSUpgradeable.sol"; diff --git a/src/helpers/ValidationDataHelpers.sol b/src/helpers/ValidationDataHelpers.sol index 69c0238d..e3c9bd2d 100644 --- a/src/helpers/ValidationDataHelpers.sol +++ b/src/helpers/ValidationDataHelpers.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; /// @dev This helper function assumes that uint160(validationData1) and uint160(validationData2) can only be 0 or 1 // solhint-disable-next-line private-vars-leading-underscore diff --git a/src/interfaces/IAccountInitializable.sol b/src/interfaces/IAccountInitializable.sol index 16d08ece..335db1cf 100644 --- a/src/interfaces/IAccountInitializable.sol +++ b/src/interfaces/IAccountInitializable.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; /// @title Account Initializable Interface interface IAccountInitializable { diff --git a/src/interfaces/IAccountLoupe.sol b/src/interfaces/IAccountLoupe.sol index aeebb005..15c7adb0 100644 --- a/src/interfaces/IAccountLoupe.sol +++ b/src/interfaces/IAccountLoupe.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {FunctionReference} from "../libraries/FunctionReferenceLib.sol"; diff --git a/src/interfaces/IAccountView.sol b/src/interfaces/IAccountView.sol index f4ea3dcf..7f034576 100644 --- a/src/interfaces/IAccountView.sol +++ b/src/interfaces/IAccountView.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {IEntryPoint} from "./erc4337/IEntryPoint.sol"; diff --git a/src/interfaces/IPlugin.sol b/src/interfaces/IPlugin.sol index b5b2e2c1..f8f1acce 100644 --- a/src/interfaces/IPlugin.sol +++ b/src/interfaces/IPlugin.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {IPluginManager} from "./IPluginManager.sol"; diff --git a/src/interfaces/IPluginExecutor.sol b/src/interfaces/IPluginExecutor.sol index ca7e254a..88437749 100644 --- a/src/interfaces/IPluginExecutor.sol +++ b/src/interfaces/IPluginExecutor.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; /// @title Plugin Executor Interface interface IPluginExecutor { diff --git a/src/interfaces/IPluginManager.sol b/src/interfaces/IPluginManager.sol index 8ac143b5..512dce82 100644 --- a/src/interfaces/IPluginManager.sol +++ b/src/interfaces/IPluginManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {FunctionReference} from "../libraries/FunctionReferenceLib.sol"; diff --git a/src/interfaces/IStandardExecutor.sol b/src/interfaces/IStandardExecutor.sol index dd457435..9284a1d7 100644 --- a/src/interfaces/IStandardExecutor.sol +++ b/src/interfaces/IStandardExecutor.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; struct Call { // The target address for account to call. diff --git a/src/interfaces/erc4337/IAccount.sol b/src/interfaces/erc4337/IAccount.sol index 2b10f0ef..ec0999d0 100644 --- a/src/interfaces/erc4337/IAccount.sol +++ b/src/interfaces/erc4337/IAccount.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {IEntryPoint} from "./IEntryPoint.sol"; import {UserOperation} from "./UserOperation.sol"; diff --git a/src/interfaces/erc4337/IAggregator.sol b/src/interfaces/erc4337/IAggregator.sol index 6db959a0..f8c7e5a7 100644 --- a/src/interfaces/erc4337/IAggregator.sol +++ b/src/interfaces/erc4337/IAggregator.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {UserOperation} from "./UserOperation.sol"; diff --git a/src/interfaces/erc4337/IEntryPoint.sol b/src/interfaces/erc4337/IEntryPoint.sol index 1e011e0d..2da014e5 100644 --- a/src/interfaces/erc4337/IEntryPoint.sol +++ b/src/interfaces/erc4337/IEntryPoint.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {UserOperation} from "./UserOperation.sol"; diff --git a/src/interfaces/erc4337/IPaymaster.sol b/src/interfaces/erc4337/IPaymaster.sol index 43066c9a..e1cd8cd3 100644 --- a/src/interfaces/erc4337/IPaymaster.sol +++ b/src/interfaces/erc4337/IPaymaster.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {UserOperation} from "./UserOperation.sol"; diff --git a/src/interfaces/erc4337/UserOperation.sol b/src/interfaces/erc4337/UserOperation.sol index 1a852ea1..23fbbe07 100644 --- a/src/interfaces/erc4337/UserOperation.sol +++ b/src/interfaces/erc4337/UserOperation.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; /// @notice User Operation struct as defined in ERC-4337 struct UserOperation { diff --git a/src/libraries/AccountStorageV1.sol b/src/libraries/AccountStorageV1.sol index c419b7bb..0fccb444 100644 --- a/src/libraries/AccountStorageV1.sol +++ b/src/libraries/AccountStorageV1.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {IPlugin} from "../interfaces/IPlugin.sol"; diff --git a/src/libraries/AssociatedLinkedListSetLib.sol b/src/libraries/AssociatedLinkedListSetLib.sol index b12c9b15..9d5255df 100644 --- a/src/libraries/AssociatedLinkedListSetLib.sol +++ b/src/libraries/AssociatedLinkedListSetLib.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {SetValue, SENTINEL_VALUE, HAS_NEXT_FLAG} from "./LinkedListSetUtils.sol"; diff --git a/src/libraries/CastLib.sol b/src/libraries/CastLib.sol index 5d9a8374..19cd9fe9 100644 --- a/src/libraries/CastLib.sol +++ b/src/libraries/CastLib.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {FunctionReference} from "./FunctionReferenceLib.sol"; import {SetValue} from "./LinkedListSetUtils.sol"; diff --git a/src/libraries/CountableLinkedListSetLib.sol b/src/libraries/CountableLinkedListSetLib.sol index e1e7bb89..43d70fc5 100644 --- a/src/libraries/CountableLinkedListSetLib.sol +++ b/src/libraries/CountableLinkedListSetLib.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {LinkedListSet, LinkedListSetLib} from "./LinkedListSetLib.sol"; import {SetValue} from "./LinkedListSetUtils.sol"; diff --git a/src/libraries/FunctionReferenceLib.sol b/src/libraries/FunctionReferenceLib.sol index 145b8a66..5ee56b55 100644 --- a/src/libraries/FunctionReferenceLib.sol +++ b/src/libraries/FunctionReferenceLib.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; type FunctionReference is bytes21; diff --git a/src/libraries/LinkedListSetLib.sol b/src/libraries/LinkedListSetLib.sol index cc87f85c..478c6b4d 100644 --- a/src/libraries/LinkedListSetLib.sol +++ b/src/libraries/LinkedListSetLib.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {SetValue, SENTINEL_VALUE, HAS_NEXT_FLAG} from "./LinkedListSetUtils.sol"; diff --git a/src/libraries/LinkedListSetUtils.sol b/src/libraries/LinkedListSetUtils.sol index d2b20c79..7560ec0d 100644 --- a/src/libraries/LinkedListSetUtils.sol +++ b/src/libraries/LinkedListSetUtils.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; type SetValue is bytes30; diff --git a/src/libraries/PluginStorageLib.sol b/src/libraries/PluginStorageLib.sol index e600561e..400b002a 100644 --- a/src/libraries/PluginStorageLib.sol +++ b/src/libraries/PluginStorageLib.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; type StoragePointer is bytes32; diff --git a/src/plugins/BasePlugin.sol b/src/plugins/BasePlugin.sol index c8ffc773..a5888e89 100644 --- a/src/plugins/BasePlugin.sol +++ b/src/plugins/BasePlugin.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; diff --git a/src/plugins/TokenReceiverPlugin.sol b/src/plugins/TokenReceiverPlugin.sol index 082f2cd2..cb8c0606 100644 --- a/src/plugins/TokenReceiverPlugin.sol +++ b/src/plugins/TokenReceiverPlugin.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import {IERC777Recipient} from "@openzeppelin/contracts/interfaces/IERC777Recipient.sol"; diff --git a/src/plugins/owner/IMultiOwnerPlugin.sol b/src/plugins/owner/IMultiOwnerPlugin.sol index 7c352b2a..37d24775 100644 --- a/src/plugins/owner/IMultiOwnerPlugin.sol +++ b/src/plugins/owner/IMultiOwnerPlugin.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {UserOperation} from "../../interfaces/erc4337/UserOperation.sol"; diff --git a/src/plugins/owner/MultiOwnerPlugin.sol b/src/plugins/owner/MultiOwnerPlugin.sol index a3333a9c..dce18aee 100644 --- a/src/plugins/owner/MultiOwnerPlugin.sol +++ b/src/plugins/owner/MultiOwnerPlugin.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol"; diff --git a/src/plugins/session/ISessionKeyPlugin.sol b/src/plugins/session/ISessionKeyPlugin.sol index 362bf209..0d5f5768 100644 --- a/src/plugins/session/ISessionKeyPlugin.sol +++ b/src/plugins/session/ISessionKeyPlugin.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Call} from "../../interfaces/IStandardExecutor.sol"; import {UserOperation} from "../../interfaces/erc4337/UserOperation.sol"; diff --git a/src/plugins/session/SessionKeyPlugin.sol b/src/plugins/session/SessionKeyPlugin.sol index 4d5b0d69..9f6bedfb 100644 --- a/src/plugins/session/SessionKeyPlugin.sol +++ b/src/plugins/session/SessionKeyPlugin.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; diff --git a/src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol b/src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol index 9120644e..aec02143 100644 --- a/src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol +++ b/src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {ISessionKeyPlugin} from "../ISessionKeyPlugin.sol"; diff --git a/src/plugins/session/permissions/SessionKeyPermissions.sol b/src/plugins/session/permissions/SessionKeyPermissions.sol index 071db835..1ab90d88 100644 --- a/src/plugins/session/permissions/SessionKeyPermissions.sol +++ b/src/plugins/session/permissions/SessionKeyPermissions.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; diff --git a/src/plugins/session/permissions/SessionKeyPermissionsBase.sol b/src/plugins/session/permissions/SessionKeyPermissionsBase.sol index baf8d7d7..8decdb07 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsBase.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsBase.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {ISessionKeyPlugin} from "../ISessionKeyPlugin.sol"; diff --git a/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol b/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol index de9bfff4..900fdaaa 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {ISessionKeyPlugin} from "../ISessionKeyPlugin.sol"; import {SessionKeyPermissionsBase} from "./SessionKeyPermissionsBase.sol"; diff --git a/test/TestUtils.sol b/test/TestUtils.sol index 05b40c6c..96266376 100644 --- a/test/TestUtils.sol +++ b/test/TestUtils.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test, console} from "forge-std/Test.sol"; diff --git a/test/Utils.sol b/test/Utils.sol index 6c4a7ef0..e5297452 100644 --- a/test/Utils.sol +++ b/test/Utils.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; library Utils { function reverseAddressArray(address[] calldata array) public pure returns (address[] memory reversedArray) { diff --git a/test/account/AccountExecHooks.t.sol b/test/account/AccountExecHooks.t.sol index 0df93c74..83056336 100644 --- a/test/account/AccountExecHooks.t.sol +++ b/test/account/AccountExecHooks.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/account/AccountLoupe.t.sol b/test/account/AccountLoupe.t.sol index 0127034b..9a48224c 100644 --- a/test/account/AccountLoupe.t.sol +++ b/test/account/AccountLoupe.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/account/AccountPermittedCallHooks.t.sol b/test/account/AccountPermittedCallHooks.t.sol index 7bef307e..1fc425b9 100644 --- a/test/account/AccountPermittedCallHooks.t.sol +++ b/test/account/AccountPermittedCallHooks.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/account/AccountPreValidationHooks.t.sol b/test/account/AccountPreValidationHooks.t.sol index 1f4488ef..f59aa92f 100644 --- a/test/account/AccountPreValidationHooks.t.sol +++ b/test/account/AccountPreValidationHooks.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/account/AccountReturnData.t.sol b/test/account/AccountReturnData.t.sol index c49548b6..db57d6b7 100644 --- a/test/account/AccountReturnData.t.sol +++ b/test/account/AccountReturnData.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/account/ExecuteFromPluginPermissions.t.sol b/test/account/ExecuteFromPluginPermissions.t.sol index 8c4936bd..dec83ade 100644 --- a/test/account/ExecuteFromPluginPermissions.t.sol +++ b/test/account/ExecuteFromPluginPermissions.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test, console} from "forge-std/Test.sol"; diff --git a/test/account/ManifestValidity.t.sol b/test/account/ManifestValidity.t.sol index dd4dbdb4..3d564baf 100644 --- a/test/account/ManifestValidity.t.sol +++ b/test/account/ManifestValidity.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/account/UpgradeableModularAccount.t.sol b/test/account/UpgradeableModularAccount.t.sol index 40994022..7823f09d 100644 --- a/test/account/UpgradeableModularAccount.t.sol +++ b/test/account/UpgradeableModularAccount.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test, console} from "forge-std/Test.sol"; diff --git a/test/account/UpgradeableModularAccountPluginManager.t.sol b/test/account/UpgradeableModularAccountPluginManager.t.sol index c70c1712..178d9f6a 100644 --- a/test/account/UpgradeableModularAccountPluginManager.t.sol +++ b/test/account/UpgradeableModularAccountPluginManager.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/account/ValidationIntersection.t.sol b/test/account/ValidationIntersection.t.sol index 3b0097f6..1ea05cd8 100644 --- a/test/account/ValidationIntersection.t.sol +++ b/test/account/ValidationIntersection.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/account/phases/AccountStatePhases.t.sol b/test/account/phases/AccountStatePhases.t.sol index 5b0a1880..e189a2e7 100644 --- a/test/account/phases/AccountStatePhases.t.sol +++ b/test/account/phases/AccountStatePhases.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/account/phases/AccountStatePhasesExec.t.sol b/test/account/phases/AccountStatePhasesExec.t.sol index 8d781f89..64c84f72 100644 --- a/test/account/phases/AccountStatePhasesExec.t.sol +++ b/test/account/phases/AccountStatePhasesExec.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {AccountStatePhasesTest} from "./AccountStatePhases.t.sol"; diff --git a/test/account/phases/AccountStatePhasesRTValidation.t.sol b/test/account/phases/AccountStatePhasesRTValidation.t.sol index 521ea0aa..5c4dbc1c 100644 --- a/test/account/phases/AccountStatePhasesRTValidation.t.sol +++ b/test/account/phases/AccountStatePhasesRTValidation.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {AccountStatePhasesTest} from "./AccountStatePhases.t.sol"; diff --git a/test/account/phases/AccountStatePhasesUOValidation.t.sol b/test/account/phases/AccountStatePhasesUOValidation.t.sol index a4851b9c..bb8a31dd 100644 --- a/test/account/phases/AccountStatePhasesUOValidation.t.sol +++ b/test/account/phases/AccountStatePhasesUOValidation.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {AccountStatePhasesTest} from "./AccountStatePhases.t.sol"; diff --git a/test/comparison/CompareSimpleAccount.t.sol b/test/comparison/CompareSimpleAccount.t.sol index 61d2560f..3b742fb2 100644 --- a/test/comparison/CompareSimpleAccount.t.sol +++ b/test/comparison/CompareSimpleAccount.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/factory/MultiOwnerMSCAFactoryTest.t.sol b/test/factory/MultiOwnerMSCAFactoryTest.t.sol index d60b22d1..479594a7 100644 --- a/test/factory/MultiOwnerMSCAFactoryTest.t.sol +++ b/test/factory/MultiOwnerMSCAFactoryTest.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol b/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol index f5dd68d4..eebae3d5 100644 --- a/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol +++ b/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/helpers/KnownSelectors.t.sol b/test/helpers/KnownSelectors.t.sol index 6fe29662..4e881ddc 100644 --- a/test/helpers/KnownSelectors.t.sol +++ b/test/helpers/KnownSelectors.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/invariant/AssociatedLinkedListSetLibInvariants.t.sol b/test/invariant/AssociatedLinkedListSetLibInvariants.t.sol index 0ce0e02c..a0208cde 100644 --- a/test/invariant/AssociatedLinkedListSetLibInvariants.t.sol +++ b/test/invariant/AssociatedLinkedListSetLibInvariants.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/invariant/LLSLRepro.t.sol b/test/invariant/LLSLRepro.t.sol index 7102f87a..9a3f4307 100644 --- a/test/invariant/LLSLRepro.t.sol +++ b/test/invariant/LLSLRepro.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/invariant/LinkedListSetLibInvariants.t.sol b/test/invariant/LinkedListSetLibInvariants.t.sol index 8041e155..b6787cfd 100644 --- a/test/invariant/LinkedListSetLibInvariants.t.sol +++ b/test/invariant/LinkedListSetLibInvariants.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/invariant/handlers/AssociatedLinkedListSetHandler.sol b/test/invariant/handlers/AssociatedLinkedListSetHandler.sol index 00a1e916..525c6f0c 100644 --- a/test/invariant/handlers/AssociatedLinkedListSetHandler.sol +++ b/test/invariant/handlers/AssociatedLinkedListSetHandler.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {CommonBase} from "forge-std/Base.sol"; import {StdCheats} from "forge-std/StdCheats.sol"; diff --git a/test/invariant/handlers/LinkedListSetHandler.sol b/test/invariant/handlers/LinkedListSetHandler.sol index 526e8c41..77ccba2f 100644 --- a/test/invariant/handlers/LinkedListSetHandler.sol +++ b/test/invariant/handlers/LinkedListSetHandler.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {CommonBase} from "forge-std/Base.sol"; import {StdCheats} from "forge-std/StdCheats.sol"; diff --git a/test/libraries/AccountStorage.t.sol b/test/libraries/AccountStorage.t.sol index ae91dc34..16847ae7 100644 --- a/test/libraries/AccountStorage.t.sol +++ b/test/libraries/AccountStorage.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; import {AccountStorageV1} from "../../src/libraries/AccountStorageV1.sol"; diff --git a/test/libraries/AssociatedLinkedListSetLib.t.sol b/test/libraries/AssociatedLinkedListSetLib.t.sol index ef8ed6a4..74b62f34 100644 --- a/test/libraries/AssociatedLinkedListSetLib.t.sol +++ b/test/libraries/AssociatedLinkedListSetLib.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/libraries/CountableLinkedListSetLib.t.sol b/test/libraries/CountableLinkedListSetLib.t.sol index d5f8bc9f..c09deadb 100644 --- a/test/libraries/CountableLinkedListSetLib.t.sol +++ b/test/libraries/CountableLinkedListSetLib.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/libraries/FunctionReferenceLib.t.sol b/test/libraries/FunctionReferenceLib.t.sol index c71a2bff..b019504e 100644 --- a/test/libraries/FunctionReferenceLib.t.sol +++ b/test/libraries/FunctionReferenceLib.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; import {FunctionReference, FunctionReferenceLib} from "../../src/libraries/FunctionReferenceLib.sol"; diff --git a/test/libraries/LinkedListSetLib.t.sol b/test/libraries/LinkedListSetLib.t.sol index e6b2df4e..806e28ef 100644 --- a/test/libraries/LinkedListSetLib.t.sol +++ b/test/libraries/LinkedListSetLib.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/libraries/PluginStorageLib.t.sol b/test/libraries/PluginStorageLib.t.sol index e9226ad7..8640b407 100644 --- a/test/libraries/PluginStorageLib.t.sol +++ b/test/libraries/PluginStorageLib.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/mocks/ContractOwner.sol b/test/mocks/ContractOwner.sol index 5ed0c3f9..e96f8044 100644 --- a/test/mocks/ContractOwner.sol +++ b/test/mocks/ContractOwner.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; diff --git a/test/mocks/Counter.sol b/test/mocks/Counter.sol index 114403aa..3ee416bb 100644 --- a/test/mocks/Counter.sol +++ b/test/mocks/Counter.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; /// @title A public counter for anyone to use. contract Counter { diff --git a/test/mocks/Counter.t.sol b/test/mocks/Counter.t.sol index 7c63181b..01b3d793 100644 --- a/test/mocks/Counter.t.sol +++ b/test/mocks/Counter.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; import {Counter} from "./Counter.sol"; diff --git a/test/mocks/MockDiamondStorageContract.sol b/test/mocks/MockDiamondStorageContract.sol index 76e47dac..e71e4379 100644 --- a/test/mocks/MockDiamondStorageContract.sol +++ b/test/mocks/MockDiamondStorageContract.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {AccountStorageInitializable} from "../../src/account/AccountStorageInitializable.sol"; diff --git a/test/mocks/MockPlugin.sol b/test/mocks/MockPlugin.sol index ae4e263c..3d7e4260 100644 --- a/test/mocks/MockPlugin.sol +++ b/test/mocks/MockPlugin.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; diff --git a/test/mocks/plugins/AccountStateMutatingPlugin.sol b/test/mocks/plugins/AccountStateMutatingPlugin.sol index cad12603..ae75ffbd 100644 --- a/test/mocks/plugins/AccountStateMutatingPlugin.sol +++ b/test/mocks/plugins/AccountStateMutatingPlugin.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import { PluginManifest, diff --git a/test/mocks/plugins/BadTransferOwnershipPlugin.sol b/test/mocks/plugins/BadTransferOwnershipPlugin.sol index 9c8dc3b8..c79d2309 100644 --- a/test/mocks/plugins/BadTransferOwnershipPlugin.sol +++ b/test/mocks/plugins/BadTransferOwnershipPlugin.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import { ManifestExecutionHook, diff --git a/test/mocks/plugins/BaseTestPlugin.sol b/test/mocks/plugins/BaseTestPlugin.sol index 8b28a0f0..3daf3fdb 100644 --- a/test/mocks/plugins/BaseTestPlugin.sol +++ b/test/mocks/plugins/BaseTestPlugin.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {BasePlugin} from "../../../src/plugins/BasePlugin.sol"; import {PluginMetadata} from "../../../src/interfaces/IPlugin.sol"; diff --git a/test/mocks/plugins/ChangingManifestPlugin.sol b/test/mocks/plugins/ChangingManifestPlugin.sol index ae7e1be1..7c798869 100644 --- a/test/mocks/plugins/ChangingManifestPlugin.sol +++ b/test/mocks/plugins/ChangingManifestPlugin.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; diff --git a/test/mocks/plugins/ComprehensivePlugin.sol b/test/mocks/plugins/ComprehensivePlugin.sol index 31e58419..40a363ce 100644 --- a/test/mocks/plugins/ComprehensivePlugin.sol +++ b/test/mocks/plugins/ComprehensivePlugin.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {UserOperation} from "../../../src/interfaces/erc4337/UserOperation.sol"; import { diff --git a/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol b/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol index 73b00888..276e35d0 100644 --- a/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol +++ b/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import { ManifestFunction, diff --git a/test/mocks/plugins/ManifestValidityMocks.sol b/test/mocks/plugins/ManifestValidityMocks.sol index 9353a002..474ff01c 100644 --- a/test/mocks/plugins/ManifestValidityMocks.sol +++ b/test/mocks/plugins/ManifestValidityMocks.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import { ManifestFunction, diff --git a/test/mocks/plugins/ReturnDataPluginMocks.sol b/test/mocks/plugins/ReturnDataPluginMocks.sol index 62a8ebfe..05148cf9 100644 --- a/test/mocks/plugins/ReturnDataPluginMocks.sol +++ b/test/mocks/plugins/ReturnDataPluginMocks.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import { ManifestFunction, diff --git a/test/mocks/plugins/UninstallErrorsPlugin.sol b/test/mocks/plugins/UninstallErrorsPlugin.sol index 04f121d6..205369ec 100644 --- a/test/mocks/plugins/UninstallErrorsPlugin.sol +++ b/test/mocks/plugins/UninstallErrorsPlugin.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {StorageSlot} from "@openzeppelin/contracts/utils/StorageSlot.sol"; diff --git a/test/mocks/plugins/ValidationPluginMocks.sol b/test/mocks/plugins/ValidationPluginMocks.sol index 2f80add8..9687ec8b 100644 --- a/test/mocks/plugins/ValidationPluginMocks.sol +++ b/test/mocks/plugins/ValidationPluginMocks.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {UserOperation} from "../../../src/interfaces/erc4337/UserOperation.sol"; import { diff --git a/test/mocks/tokens/MockERC1155.sol b/test/mocks/tokens/MockERC1155.sol index 82fd463d..2ec184a7 100644 --- a/test/mocks/tokens/MockERC1155.sol +++ b/test/mocks/tokens/MockERC1155.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; diff --git a/test/mocks/tokens/MockERC20.sol b/test/mocks/tokens/MockERC20.sol index 916b2b81..10b9451f 100644 --- a/test/mocks/tokens/MockERC20.sol +++ b/test/mocks/tokens/MockERC20.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; diff --git a/test/mocks/tokens/MockERC777.sol b/test/mocks/tokens/MockERC777.sol index a6478ea4..913d7795 100644 --- a/test/mocks/tokens/MockERC777.sol +++ b/test/mocks/tokens/MockERC777.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {IERC777} from "@openzeppelin/contracts/token/ERC777/IERC777.sol"; import {IERC777Recipient} from "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol"; diff --git a/test/plugin/TokenReceiverPlugin.t.sol b/test/plugin/TokenReceiverPlugin.t.sol index 1ab6543d..e9665f9f 100644 --- a/test/plugin/TokenReceiverPlugin.t.sol +++ b/test/plugin/TokenReceiverPlugin.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/plugin/owner/MultiOwnerPlugin.t.sol b/test/plugin/owner/MultiOwnerPlugin.t.sol index 52308f1e..3d68ce1d 100644 --- a/test/plugin/owner/MultiOwnerPlugin.t.sol +++ b/test/plugin/owner/MultiOwnerPlugin.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/plugin/owner/MultiOwnerPluginIntegration.t.sol b/test/plugin/owner/MultiOwnerPluginIntegration.t.sol index 19991d4a..2ffb77e7 100644 --- a/test/plugin/owner/MultiOwnerPluginIntegration.t.sol +++ b/test/plugin/owner/MultiOwnerPluginIntegration.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol index 2bd13ada..c03fb52c 100644 --- a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol +++ b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol b/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol index 951d7124..df38f956 100644 --- a/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test, console} from "forge-std/Test.sol"; diff --git a/test/plugin/session/permissions/SessionKeyGasLimits.t.sol b/test/plugin/session/permissions/SessionKeyGasLimits.t.sol index afbabb81..d0127cae 100644 --- a/test/plugin/session/permissions/SessionKeyGasLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyGasLimits.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test, console} from "forge-std/Test.sol"; diff --git a/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol b/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol index 595cea56..f1a0245c 100644 --- a/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test, console} from "forge-std/Test.sol"; diff --git a/test/plugin/session/permissions/SessionKeyPermissions.t.sol b/test/plugin/session/permissions/SessionKeyPermissions.t.sol index 2b4cc14c..6dcdb2a1 100644 --- a/test/plugin/session/permissions/SessionKeyPermissions.t.sol +++ b/test/plugin/session/permissions/SessionKeyPermissions.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/upgrade/LightAccountToMSCA.t.sol b/test/upgrade/LightAccountToMSCA.t.sol index 2e7b3ce7..4476fa8e 100644 --- a/test/upgrade/LightAccountToMSCA.t.sol +++ b/test/upgrade/LightAccountToMSCA.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/upgrade/MSCAToMSCA.t.sol b/test/upgrade/MSCAToMSCA.t.sol index 67da13a4..6a571d6f 100644 --- a/test/upgrade/MSCAToMSCA.t.sol +++ b/test/upgrade/MSCAToMSCA.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.21; +pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; From 1d29c8fd68ae30e2284e53b7b0cc1d2f01fb424b Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Mon, 22 Jan 2024 09:16:39 -0800 Subject: [PATCH 072/106] fix: [spearbit-16] Use _assertNotNullFunction in `_addHooks` (#83) --- src/account/PluginManagerInternals.sol | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/account/PluginManagerInternals.sol b/src/account/PluginManagerInternals.sol index 45147b5a..b5619cc7 100644 --- a/src/account/PluginManagerInternals.sol +++ b/src/account/PluginManagerInternals.sol @@ -224,10 +224,8 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { } } } else { - if (postExecHook == FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE) { - // both pre and post hooks cannot be null - revert NullFunctionReference(); - } + // both pre and post hooks cannot be null + _assertNotNullFunction(postExecHook); if (!hooks.postOnlyHooks.tryIncrement(CastLib.toSetValue(postExecHook))) { revert DuplicateHookLimitExceeded(selector, postExecHook); From fead83dbb8b610879c92d4806c78cd396194849f Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Mon, 22 Jan 2024 09:29:03 -0800 Subject: [PATCH 073/106] fix: [spearbit-82] Session key init perms (#80) --- src/plugins/session/ISessionKeyPlugin.sol | 4 +- src/plugins/session/SessionKeyPlugin.sol | 41 ++++++- .../permissions/SessionKeyPermissions.sol | 2 +- ...gradeableModularAccountPluginManager.t.sol | 6 +- .../SessionKeyPluginWithMultiOwner.t.sol | 90 ++++++++++++---- .../SessionKeyERC20SpendLimits.t.sol | 4 +- .../permissions/SessionKeyGasLimits.t.sol | 4 +- .../SessionKeyNativeTokenSpendLimits.t.sol | 4 +- .../permissions/SessionKeyPermissions.t.sol | 100 +++++++++++++++++- 9 files changed, 215 insertions(+), 40 deletions(-) diff --git a/src/plugins/session/ISessionKeyPlugin.sol b/src/plugins/session/ISessionKeyPlugin.sol index 0d5f5768..85e35287 100644 --- a/src/plugins/session/ISessionKeyPlugin.sol +++ b/src/plugins/session/ISessionKeyPlugin.sol @@ -59,6 +59,7 @@ interface ISessionKeyPlugin { error InvalidPermissionsUpdate(); error InvalidToken(); error NativeTokenSpendLimitExceeded(address account, address sessionKey); + error LengthMismatch(); // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ // ┃ Execution functions ┃ @@ -75,7 +76,8 @@ interface ISessionKeyPlugin { /// @notice Add a session key. /// @param sessionKey The session key to register. /// @param tag An optional tag that can be used to identify the key. - function addSessionKey(address sessionKey, bytes32 tag) external; + /// @param permissionUpdates The initial permission updates to apply to the key. + function addSessionKey(address sessionKey, bytes32 tag, bytes[] calldata permissionUpdates) external; /// @notice Remove a session key. /// @param sessionKey The session key to remove. diff --git a/src/plugins/session/SessionKeyPlugin.sol b/src/plugins/session/SessionKeyPlugin.sol index 9f6bedfb..cec2e47d 100644 --- a/src/plugins/session/SessionKeyPlugin.sol +++ b/src/plugins/session/SessionKeyPlugin.sol @@ -122,7 +122,7 @@ contract SessionKeyPlugin is ISessionKeyPlugin, SessionKeyPermissions, BasePlugi } /// @inheritdoc ISessionKeyPlugin - function addSessionKey(address sessionKey, bytes32 tag) public override { + function addSessionKey(address sessionKey, bytes32 tag, bytes[] calldata permissionUpdates) public override { if (!_sessionKeys.tryAdd(msg.sender, CastLib.toSetValue(sessionKey))) { // This check ensures no duplicate keys and that the session key is not the zero address. revert InvalidSessionKey(sessionKey); @@ -135,6 +135,11 @@ contract SessionKeyPlugin is ISessionKeyPlugin, SessionKeyPermissions, BasePlugi _updateSessionKeyId(msg.sender, sessionKey, SessionKeyId.wrap(bytes32(++_keyIdCounter[msg.sender]))); emit SessionKeyAdded(msg.sender, sessionKey, tag); + + if (permissionUpdates.length > 0) { + // Call the public function internally to update the permissions. + updateKeyPermissions(sessionKey, permissionUpdates); + } } /// @inheritdoc ISessionKeyPlugin @@ -357,13 +362,39 @@ contract SessionKeyPlugin is ISessionKeyPlugin, SessionKeyPermissions, BasePlugi /// @inheritdoc BasePlugin function _onInstall(bytes calldata data) internal override isNotInitialized(msg.sender) { - address[] memory sessionKeysToAdd = abi.decode(data, (address[])); + (address[] memory sessionKeysToAdd, bytes32[] memory tags,) = + abi.decode(data, (address[], bytes32[], bytes[][])); + + // Permission updates depend on `calldata` types for the updates, in order to use array slices. + // Unfortunately, solidity does not support `abi.decode` outputting calldata types, so we do it manually. + // The prior `abi.decode` call validates that `data` has a valid encoding of a `bytes[][]` at this + // position, so we can load in the offset and length of the `bytes[][]` directly. + bytes[][] calldata permissionUpdates; + assembly ("memory-safe") { + // Get the offset of the bytes[][] used for permissions updates. The offset for this field is stored at + // the third word of `data`. Note that `data.offset` refers to the start of the actual data contents, + // one word after the length. + let permissionUpdatesRelativeOffset := calldataload(add(data.offset, 0x40)) + // We now have the relative offset of the bytes[][] in `data`. We need to add the starting offset + // (aka `data.offset`) to get the absolute offset. + let permissionUpdatesLengthOffset := add(data.offset, permissionUpdatesRelativeOffset) + // Note that solidity expects the field `var.offset` to point to the start of the actual data contents, + // one word after the length. Therefore, we add 0x20 here to get the correct offset. + permissionUpdates.offset := add(0x20, permissionUpdatesLengthOffset) + // Load the length of the bytes[][]. + permissionUpdates.length := calldataload(permissionUpdatesLengthOffset) + } uint256 length = sessionKeysToAdd.length; + + if (length != tags.length || length != permissionUpdates.length) { + revert LengthMismatch(); + } + for (uint256 i = 0; i < length; ++i) { - // Use the public function to add the session key, set the key id, and emit the event. - // Note that no tags are set when adding keys with this method. - addSessionKey(sessionKeysToAdd[i], bytes32(0)); + // Use the public function to add the session key, set the key id, emit the event, and update the + // permissions. + addSessionKey(sessionKeysToAdd[i], tags[i], permissionUpdates[i]); } } diff --git a/src/plugins/session/permissions/SessionKeyPermissions.sol b/src/plugins/session/permissions/SessionKeyPermissions.sol index 1ab90d88..87e354e4 100644 --- a/src/plugins/session/permissions/SessionKeyPermissions.sol +++ b/src/plugins/session/permissions/SessionKeyPermissions.sol @@ -21,7 +21,7 @@ abstract contract SessionKeyPermissions is ISessionKeyPlugin, SessionKeyPermissi // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ /// @inheritdoc ISessionKeyPlugin - function updateKeyPermissions(address sessionKey, bytes[] calldata updates) external override { + function updateKeyPermissions(address sessionKey, bytes[] calldata updates) public override { (SessionKeyData storage sessionKeyData, SessionKeyId keyId) = _loadSessionKey(msg.sender, sessionKey); uint256 length = updates.length; diff --git a/test/account/UpgradeableModularAccountPluginManager.t.sol b/test/account/UpgradeableModularAccountPluginManager.t.sol index 178d9f6a..9bc337f4 100644 --- a/test/account/UpgradeableModularAccountPluginManager.t.sol +++ b/test/account/UpgradeableModularAccountPluginManager.t.sol @@ -115,7 +115,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { factory.createAccount(0, owners1); } - function test_installPlugin() public { + function test_installPlugin_standard() public { vm.startPrank(owner2); bytes32 manifestHash = keccak256(abi.encode(sessionKeyPlugin.pluginManifest())); @@ -136,7 +136,9 @@ contract UpgradeableModularAccountPluginManagerTest is Test { IPluginManager(account2).installPlugin({ plugin: address(sessionKeyPlugin), manifestHash: manifestHash, - pluginInitData: abi.encode(sessionKeys), + pluginInitData: abi.encode( + sessionKeys, new bytes32[](sessionKeys.length), new bytes[][](sessionKeys.length) + ), dependencies: dependencies, injectedHooks: new IPluginManager.InjectedHook[](0) }); diff --git a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol index c03fb52c..2b0dfeee 100644 --- a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol +++ b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol @@ -84,7 +84,7 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { account1.installPlugin({ plugin: address(sessionKeyPlugin), manifestHash: manifestHash, - pluginInitData: abi.encode(new address[](0)), + pluginInitData: abi.encode(new address[](0), new bytes32[](0), new bytes[][](0)), dependencies: dependencies, injectedHooks: new IPluginManager.InjectedHook[](0) }); @@ -96,7 +96,7 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { vm.expectEmit(true, true, true, true); emit SessionKeyAdded(address(account1), sessionKeyToAdd, bytes32(0)); vm.prank(owner1); - SessionKeyPlugin(address(account1)).addSessionKey(sessionKeyToAdd, bytes32(0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKeyToAdd, bytes32(0), new bytes[](0)); // Check using all view methods address[] memory sessionKeys = sessionKeyPlugin.sessionKeysOf(address(account1)); @@ -111,13 +111,13 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { // Zero address session key address sessionKeyToAdd = address(0); vm.expectRevert(abi.encodeWithSelector(ISessionKeyPlugin.InvalidSessionKey.selector, address(0))); - SessionKeyPlugin(address(account1)).addSessionKey(sessionKeyToAdd, bytes32(0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKeyToAdd, bytes32(0), new bytes[](0)); // Duplicate session key sessionKeyToAdd = makeAddr("sessionKey1"); - SessionKeyPlugin(address(account1)).addSessionKey(sessionKeyToAdd, bytes32(0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKeyToAdd, bytes32(0), new bytes[](0)); vm.expectRevert(abi.encodeWithSelector(ISessionKeyPlugin.InvalidSessionKey.selector, sessionKeyToAdd)); - SessionKeyPlugin(address(account1)).addSessionKey(sessionKeyToAdd, bytes32(0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKeyToAdd, bytes32(0), new bytes[](0)); // Check using all view methods address[] memory sessionKeys = sessionKeyPlugin.sessionKeysOf(address(account1)); @@ -131,8 +131,8 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { address sessionKey2 = makeAddr("sessionKey2"); vm.startPrank(owner1); - SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); - SessionKeyPlugin(address(account1)).addSessionKey(sessionKey2, bytes32(0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0), new bytes[](0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey2, bytes32(0), new bytes[](0)); vm.stopPrank(); // Check using all view methods @@ -159,11 +159,61 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { assertTrue(sessionKeyPlugin.isSessionKeyOf(address(account1), sessionKey2)); } + function testFuzz_sessionKey_addKeysDuringInstall(uint8 seed) public { + // First uninstall the plugin + vm.prank(owner1); + account1.uninstallPlugin(address(sessionKeyPlugin), "", "", new bytes[](0)); + + // Generate a set of initial session keys + uint256 addressCount = (seed % 16) + 1; + + address[] memory sessionKeysToAdd = new address[](addressCount); + bytes32[] memory tags = new bytes32[](addressCount); + for (uint256 i = 0; i < addressCount; i++) { + sessionKeysToAdd[i] = makeAddr(string.concat(vm.toString(seed), "sessionKey", vm.toString(i))); + tags[i] = bytes32(uint256(i) + seed); + } + + bytes memory onInstallData = abi.encode(sessionKeysToAdd, tags, new bytes[][](addressCount)); + + // Re-install the plugin + bytes32 manifestHash = keccak256(abi.encode(sessionKeyPlugin.pluginManifest())); + FunctionReference[] memory dependencies = new FunctionReference[](2); + dependencies[0] = FunctionReferenceLib.pack( + address(multiOwnerPlugin), uint8(IMultiOwnerPlugin.FunctionId.USER_OP_VALIDATION_OWNER) + ); + dependencies[1] = FunctionReferenceLib.pack( + address(multiOwnerPlugin), uint8(IMultiOwnerPlugin.FunctionId.RUNTIME_VALIDATION_OWNER_OR_SELF) + ); + + for (uint256 i = 0; i < addressCount; i++) { + vm.expectEmit(true, true, true, true); + emit SessionKeyAdded(address(account1), sessionKeysToAdd[i], tags[i]); + } + vm.prank(owner1); + account1.installPlugin({ + plugin: address(sessionKeyPlugin), + manifestHash: manifestHash, + pluginInitData: onInstallData, + dependencies: dependencies, + injectedHooks: new IPluginManager.InjectedHook[](0) + }); + + // Check using all view methods + address[] memory sessionKeys = sessionKeyPlugin.sessionKeysOf(address(account1)); + assertEq(sessionKeys.length, addressCount); + for (uint256 i = 0; i < addressCount; i++) { + // Invert the indexing because the view function will return it in reverse order + assertEq(sessionKeys[sessionKeys.length - 1 - i], sessionKeysToAdd[i]); + assertTrue(sessionKeyPlugin.isSessionKeyOf(address(account1), sessionKeysToAdd[i])); + } + } + function test_sessionKey_rotate_valid() public { // Add the first key address sessionKey1 = makeAddr("sessionKey1"); vm.prank(owner1); - SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0), new bytes[](0)); // Rotate to the second key address sessionKey2 = makeAddr("sessionKey2"); @@ -186,8 +236,8 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { address sessionKey1 = makeAddr("sessionKey1"); address sessionKey2 = makeAddr("sessionKey2"); vm.startPrank(owner1); - SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); - SessionKeyPlugin(address(account1)).addSessionKey(sessionKey2, bytes32(0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0), new bytes[](0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey2, bytes32(0), new bytes[](0)); vm.stopPrank(); // Attempt to rotate key 1 to key 2 @@ -201,7 +251,7 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { // Add the first key address sessionKey1 = makeAddr("sessionKey1"); vm.prank(owner1); - SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0), new bytes[](0)); // Attempt to rotate to the zero address address zeroAddr = address(0); @@ -215,7 +265,7 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { (address sessionKey1, uint256 sessionKeyPrivate) = makeAddrAndKey("sessionKey1"); vm.startPrank(owner1); - SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0), new bytes[](0)); // Disable the allowlist and native token spend checking bytes[] memory permissionUpdates = new bytes[](2); permissionUpdates[0] = abi.encodeCall( @@ -261,7 +311,7 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { (address sessionKey1,) = makeAddrAndKey("sessionKey1"); vm.startPrank(owner1); - SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0), new bytes[](0)); Call[] memory calls = new Call[](1); calls[0] = Call({target: recipient, value: 1 wei, data: ""}); @@ -401,8 +451,8 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { address sessionKey2 = makeAddr("sessionKey2"); vm.startPrank(owner1); - SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); - SessionKeyPlugin(address(account1)).addSessionKey(sessionKey2, bytes32(0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0), new bytes[](0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey2, bytes32(0), new bytes[](0)); bytes32 predecessor = sessionKeyPlugin.findPredecessor(address(account1), sessionKey2); assertEq(predecessor, bytes32(uint256(1))); @@ -424,8 +474,8 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { address sessionKey2 = makeAddr("sessionKey2"); vm.startPrank(owner1); - SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); - SessionKeyPlugin(address(account1)).addSessionKey(sessionKey2, bytes32(0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0), new bytes[](0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey2, bytes32(0), new bytes[](0)); bytes32 predecessor = sessionKeyPlugin.findPredecessor(address(account1), sessionKey1); assertEq(predecessor, bytes32(bytes20(sessionKey2))); @@ -446,7 +496,7 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { sessionKeysToAdd[0] = makeAddr("sessionKey1"); vm.startPrank(owner1); - SessionKeyPlugin(address(account1)).addSessionKey(sessionKeysToAdd[0], bytes32(0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKeysToAdd[0], bytes32(0), new bytes[](0)); address key2 = makeAddr("sessionKey2"); vm.expectRevert(abi.encodeWithSelector(ISessionKeyPlugin.SessionKeyNotFound.selector, key2)); @@ -460,7 +510,7 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { sessionKeysToAdd[0] = makeAddr("sessionKey1"); vm.prank(owner1); - SessionKeyPlugin(address(account1)).addSessionKey(sessionKeysToAdd[0], bytes32(0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKeysToAdd[0], bytes32(0), new bytes[](0)); assertFalse(sessionKeyPlugin.isSessionKeyOf(address(account1), address(1))); } @@ -484,7 +534,7 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { vm.startPrank(owner1); for (uint256 i = 0; i < addressCount; i++) { - SessionKeyPlugin(address(account1)).addSessionKey(sessionKeysToAdd[i], bytes32(0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKeysToAdd[i], bytes32(0), new bytes[](0)); SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKeysToAdd[i], permissionUpdates); } vm.stopPrank(); diff --git a/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol b/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol index df38f956..163234ef 100644 --- a/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol @@ -86,7 +86,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { account1.installPlugin({ plugin: address(sessionKeyPlugin), manifestHash: manifestHash, - pluginInitData: abi.encode(new address[](0)), + pluginInitData: abi.encode(new address[](0), new bytes32[](0), new bytes[][](0)), dependencies: dependencies, injectedHooks: new IPluginManager.InjectedHook[](0) }); @@ -95,7 +95,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { (sessionKey1, sessionKey1Private) = makeAddrAndKey("sessionKey1"); vm.prank(owner1); - SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0), new bytes[](0)); // Disable the allowlist bytes[] memory updates = new bytes[](1); diff --git a/test/plugin/session/permissions/SessionKeyGasLimits.t.sol b/test/plugin/session/permissions/SessionKeyGasLimits.t.sol index d0127cae..d3ceb443 100644 --- a/test/plugin/session/permissions/SessionKeyGasLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyGasLimits.t.sol @@ -77,7 +77,7 @@ contract SessionKeyGasLimitsTest is Test { account1.installPlugin({ plugin: address(sessionKeyPlugin), manifestHash: manifestHash, - pluginInitData: abi.encode(new address[](0)), + pluginInitData: abi.encode(new address[](0), new bytes32[](0), new bytes[][](0)), dependencies: dependencies, injectedHooks: new IPluginManager.InjectedHook[](0) }); @@ -86,7 +86,7 @@ contract SessionKeyGasLimitsTest is Test { (sessionKey1, sessionKey1Private) = makeAddrAndKey("sessionKey1"); vm.prank(owner1); - SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0), new bytes[](0)); bytes[] memory updates = new bytes[](1); updates[0] = abi.encodeCall( diff --git a/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol b/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol index f1a0245c..19f23cfa 100644 --- a/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol @@ -82,7 +82,7 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { account1.installPlugin({ plugin: address(sessionKeyPlugin), manifestHash: manifestHash, - pluginInitData: abi.encode(new address[](0)), + pluginInitData: abi.encode(new address[](0), new bytes32[](0), new bytes[][](0)), dependencies: dependencies, injectedHooks: new IPluginManager.InjectedHook[](0) }); @@ -91,7 +91,7 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { (sessionKey1, sessionKey1Private) = makeAddrAndKey("sessionKey1"); vm.prank(owner1); - SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0), new bytes[](0)); // Remove the allowlist bytes[] memory updates = new bytes[](1); diff --git a/test/plugin/session/permissions/SessionKeyPermissions.t.sol b/test/plugin/session/permissions/SessionKeyPermissions.t.sol index 6dcdb2a1..8cd62be4 100644 --- a/test/plugin/session/permissions/SessionKeyPermissions.t.sol +++ b/test/plugin/session/permissions/SessionKeyPermissions.t.sol @@ -48,6 +48,8 @@ contract SessionKeyPermissionsTest is Test { Counter counter2; + event PermissionsUpdated(address indexed account, address indexed sessionKey, bytes[] updates); + function setUp() public { entryPoint = IEntryPoint(address(new EntryPoint())); (owner1, owner1Key) = makeAddrAndKey("owner1"); @@ -90,7 +92,7 @@ contract SessionKeyPermissionsTest is Test { account1.installPlugin({ plugin: address(sessionKeyPlugin), manifestHash: manifestHash, - pluginInitData: abi.encode(new address[](0)), + pluginInitData: abi.encode(new address[](0), new bytes32[](0), new bytes[][](0)), dependencies: dependencies, injectedHooks: new IPluginManager.InjectedHook[](0) }); @@ -99,7 +101,7 @@ contract SessionKeyPermissionsTest is Test { (sessionKey1, sessionKey1Private) = makeAddrAndKey("sessionKey1"); vm.prank(owner1); - SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0), new bytes[](0)); // Initialize the interaction targets counter1 = new Counter(); @@ -557,7 +559,7 @@ contract SessionKeyPermissionsTest is Test { address sessionKey2 = makeAddr("sessionKey2"); vm.prank(owner1); - SessionKeyPlugin(address(account1)).addSessionKey(sessionKey2, bytes32(0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey2, bytes32(0), new bytes[](0)); ISessionKeyPlugin.ContractAccessControlType accessControlType1; ISessionKeyPlugin.ContractAccessControlType accessControlType2; @@ -624,7 +626,7 @@ contract SessionKeyPermissionsTest is Test { account1.installPlugin({ plugin: address(sessionKeyPlugin), manifestHash: keccak256(abi.encode(sessionKeyPlugin.pluginManifest())), - pluginInitData: abi.encode(new address[](0)), + pluginInitData: abi.encode(new address[](0), new bytes32[](0), new bytes[][](0)), dependencies: dependencies, injectedHooks: new IPluginManager.InjectedHook[](0) }); @@ -632,7 +634,7 @@ contract SessionKeyPermissionsTest is Test { // Re-add the session key vm.prank(owner1); - SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0)); + SessionKeyPlugin(address(account1)).addSessionKey(sessionKey1, bytes32(0), new bytes[](0)); // Assert that the time range is reset (returnedStartTime, returnedEndTime) = sessionKeyPlugin.getKeyTimeRange(address(account1), sessionKey1); @@ -640,6 +642,38 @@ contract SessionKeyPermissionsTest is Test { assertEq(returnedEndTime, uint48(0)); } + function testFuzz_initialSessionKeysWithPermissions(uint256 seed) public { + // Uninstall the plugin + vm.prank(owner1); + account1.uninstallPlugin(address(sessionKeyPlugin), "", "", new bytes[](0)); + + address[] memory sessionKeys = _generateRandomAddresses(seed); + bytes32[] memory tags = new bytes32[](sessionKeys.length); + bytes[][] memory sessionKeyPermissions = new bytes[][](sessionKeys.length); + for (uint256 i = 0; i < sessionKeys.length; i++) { + uint256 modifiedSeed; + unchecked { + modifiedSeed = seed + i; + } + sessionKeyPermissions[i] = _generateRandomPermissionUpdates(modifiedSeed); + } + + // Reinstall the plugin with the session keys + for (uint256 i = 0; i < sessionKeys.length; i++) { + vm.expectEmit(true, true, true, true); + emit PermissionsUpdated(address(account1), sessionKeys[i], sessionKeyPermissions[i]); + } + bytes32 manifestHash = keccak256(abi.encode(sessionKeyPlugin.pluginManifest())); + vm.prank(owner1); + account1.installPlugin({ + plugin: address(sessionKeyPlugin), + manifestHash: manifestHash, + pluginInitData: abi.encode(sessionKeys, tags, sessionKeyPermissions), + dependencies: dependencies, + injectedHooks: new IPluginManager.InjectedHook[](0) + }); + } + function _runSessionKeyExecUserOp( address target, address sessionKey, @@ -677,4 +711,60 @@ contract SessionKeyPermissionsTest is Test { } entryPoint.handleOps(userOps, beneficiary); } + + function _generateRandomAddresses(uint256 seed) internal returns (address[] memory keys) { + uint256 addressCount = (seed % 5) + 1; + + keys = new address[](addressCount); + for (uint256 i = 0; i < addressCount; i++) { + keys[i] = makeAddr(string.concat(vm.toString(seed), "sessionKey", vm.toString(i))); + } + } + + function _generateRandomPermissionUpdates(uint256 seed) internal returns (bytes[] memory updates) { + uint256 updateCount = (seed % 5) + 1; + + updates = new bytes[](updateCount); + + for (uint256 i = 0; i < updateCount; i++) { + uint256 updateType = (seed % 6) + 1; + if (updateType == 1) { + // Set access list type + uint256 accessListType = (seed % 3); + updates[i] = abi.encodeCall( + ISessionKeyPermissionsUpdates.setAccessListType, + ISessionKeyPlugin.ContractAccessControlType(accessListType) + ); + } else if (updateType == 2) { + // Update access list address entry + address addr = makeAddr(string.concat(vm.toString(seed), "addr", vm.toString(i))); + bool isOnList = (seed % 2) == 0; + bool checkSelectors = (seed % 3) == 0; + updates[i] = abi.encodeCall( + ISessionKeyPermissionsUpdates.updateAccessListAddressEntry, (addr, isOnList, checkSelectors) + ); + } else if (updateType == 3) { + // Update access list function entry + address addr = makeAddr(string.concat(vm.toString(seed), "addr", vm.toString(i))); + bytes4 selector = bytes4(uint32(seed)); + bool isOnList = (seed % 2) == 0; + updates[i] = abi.encodeCall( + ISessionKeyPermissionsUpdates.updateAccessListFunctionEntry, (addr, selector, isOnList) + ); + } else if (updateType == 4) { + // Set time range + uint48 startTime = uint48(seed); + uint48 endTime = uint48(seed << 2); + updates[i] = abi.encodeCall(ISessionKeyPermissionsUpdates.updateTimeRange, (startTime, endTime)); + } else if (updateType == 5) { + // Set required paymaster + address paymaster = makeAddr(string.concat(vm.toString(seed), "paymaster", vm.toString(i))); + updates[i] = abi.encodeCall(ISessionKeyPermissionsUpdates.setRequiredPaymaster, (paymaster)); + } else if (updateType == 6) { + // Set native token spend limit + uint256 limit = seed; + updates[i] = abi.encodeCall(ISessionKeyPermissionsUpdates.setNativeTokenSpendLimit, (limit, 0)); + } + } + } } From a37331daa0f8d63e24de76ed3b9a1703cb128f60 Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Mon, 22 Jan 2024 10:32:51 -0800 Subject: [PATCH 074/106] feat: remove permitted call hooks from MSCA (rebase on cache PR) (#76) Co-authored-by: Jay Paik --- src/account/AccountExecutor.sol | 1 - src/account/AccountLoupe.sol | 16 +- src/account/PluginManagerInternals.sol | 211 +---- src/account/UpgradeableModularAccount.sol | 180 +--- src/helpers/KnownSelectors.sol | 4 +- src/interfaces/IAccountLoupe.sol | 9 - src/interfaces/IPlugin.sol | 25 - src/interfaces/IPluginManager.sol | 40 +- src/libraries/AccountStorageV1.sol | 29 +- src/plugins/BasePlugin.sol | 27 - test/account/AccountExecHooks.t.sol | 24 +- test/account/AccountLoupe.t.sol | 79 +- test/account/AccountPermittedCallHooks.t.sol | 871 ------------------ test/account/AccountPreValidationHooks.t.sol | 38 +- test/account/AccountReturnData.t.sol | 7 +- .../ExecuteFromPluginPermissions.t.sol | 75 +- test/account/ManifestValidity.t.sol | 25 +- test/account/UpgradeableModularAccount.t.sol | 8 +- ...gradeableModularAccountPluginManager.t.sol | 469 +--------- test/account/ValidationIntersection.t.sol | 10 +- test/account/phases/AccountStatePhases.t.sol | 22 +- .../phases/AccountStatePhasesExec.t.sol | 65 +- .../AccountStatePhasesRTValidation.t.sol | 65 +- .../AccountStatePhasesUOValidation.t.sol | 67 +- test/helpers/KnownSelectors.t.sol | 3 - .../plugins/BadTransferOwnershipPlugin.sol | 1 - test/mocks/plugins/ComprehensivePlugin.sol | 28 - .../ExecFromPluginPermissionsMocks.sol | 127 --- test/mocks/plugins/UninstallErrorsPlugin.sol | 9 - test/plugin/TokenReceiverPlugin.t.sol | 7 +- .../owner/MultiOwnerPluginIntegration.t.sol | 1 - .../SessionKeyPluginWithMultiOwner.t.sol | 9 +- .../SessionKeyERC20SpendLimits.t.sol | 4 +- .../permissions/SessionKeyGasLimits.t.sol | 4 +- .../SessionKeyNativeTokenSpendLimits.t.sol | 4 +- .../permissions/SessionKeyPermissions.t.sol | 14 +- test/upgrade/MSCAToMSCA.t.sol | 3 +- 37 files changed, 221 insertions(+), 2360 deletions(-) delete mode 100644 test/account/AccountPermittedCallHooks.t.sol diff --git a/src/account/AccountExecutor.sol b/src/account/AccountExecutor.sol index 76644353..bbfbd305 100644 --- a/src/account/AccountExecutor.sol +++ b/src/account/AccountExecutor.sol @@ -40,7 +40,6 @@ abstract contract AccountExecutor { /// - Pre Runtime Validation Hook /// - Runtime Validation /// - Pre Execution Hook - /// - Pre Permitted Call Hook /// And if it fails, reverts with the appropriate custom error. function _executeRuntimePluginFunction(bytes memory buffer, address plugin, bytes4 errorSelector) internal { if (!_executeRaw(plugin, buffer)) { diff --git a/src/account/AccountLoupe.sol b/src/account/AccountLoupe.sol index c9c87c4d..8a8d8131 100644 --- a/src/account/AccountLoupe.sol +++ b/src/account/AccountLoupe.sol @@ -41,18 +41,6 @@ abstract contract AccountLoupe is IAccountLoupe, AccountStorageV1 { execHooks = _getHooks(_getAccountStorage().selectorData[selector].executionHooks); } - /// @inheritdoc IAccountLoupe - function getPermittedCallHooks(address callingPlugin, bytes4 selector) - external - view - returns (ExecutionHooks[] memory execHooks) - { - PermittedCallData storage permittedCallData = - _getAccountStorage().permittedCalls[_getPermittedCallKey(callingPlugin, selector)]; - - execHooks = _getHooks(permittedCallData.permittedCallHooks); - } - /// @inheritdoc IAccountLoupe function getPreValidationHooks(bytes4 selector) external @@ -73,8 +61,8 @@ abstract contract AccountLoupe is IAccountLoupe, AccountStorageV1 { pluginAddresses = CastLib.toAddressArray(_getAccountStorage().plugins.getAll()); } - /// @dev Collects hook data from stored hooks (either execution hooks or permitted call hooks) and prepares it - /// for returning as the `ExecutionHooks` type defined by `IAccountLoupe`. + /// @dev Collects hook data from stored execution hooks and prepares it for returning as the `ExecutionHooks` + /// type defined by `IAccountLoupe`. function _getHooks(HookGroup storage storedHooks) internal view returns (ExecutionHooks[] memory execHooks) { FunctionReference[] memory preExecHooks = CastLib.toFunctionReferenceArray(storedHooks.preHooks.getAll()); FunctionReference[] memory postOnlyExecHooks = diff --git a/src/account/PluginManagerInternals.sol b/src/account/PluginManagerInternals.sol index b5619cc7..50584ee1 100644 --- a/src/account/PluginManagerInternals.sol +++ b/src/account/PluginManagerInternals.sol @@ -57,9 +57,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { error NativeFunctionNotAllowed(bytes4 selector); error NullFunctionReference(); error PluginAlreadyInstalled(address plugin); - error PluginApplyHookCallbackFailed(address providingPlugin, bytes revertReason); error PluginDependencyViolation(address plugin); - error PluginHookUnapplyCallbackFailed(address providingPlugin, bytes revertReason); error PluginInstallCallbackFailed(address plugin, bytes revertReason); error PluginInterfaceNotSupported(address plugin); error PluginNotInstalled(address plugin); @@ -155,55 +153,6 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { } } - function _enableExecFromPlugin(bytes4 selector, address plugin, AccountStorage storage storage_) internal { - PermittedCallData storage permittedCallData = - storage_.permittedCalls[_getPermittedCallKey(plugin, selector)]; - - // If there are duplicates, this will just enable the flag again. This is not a problem, since the boolean - // will be set to false twice during uninstall, which is fine. - permittedCallData.callPermitted = true; - } - - function _addPermittedCallHooks( - bytes4 selector, - address plugin, - FunctionReference preExecHook, - FunctionReference postExecHook - ) internal { - PermittedCallData storage permittedCallData = - _getAccountStorage().permittedCalls[_getPermittedCallKey(plugin, selector)]; - - _addHooks(permittedCallData.permittedCallHooks, selector, preExecHook, postExecHook); - - if (preExecHook != FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE) { - permittedCallData.hasPrePermittedCallHooks = true; - } else if (postExecHook != FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE) { - // Only set this flag if the pre hook is empty and the post hook is non-empty. - permittedCallData.hasPostOnlyPermittedCallHooks = true; - } - } - - function _removePermittedCallHooks( - bytes4 selector, - address plugin, - FunctionReference preExecHook, - FunctionReference postExecHook - ) internal { - PermittedCallData storage permittedCallData = - _getAccountStorage().permittedCalls[_getPermittedCallKey(plugin, selector)]; - - (bool shouldClearHasPreHooks, bool shouldClearHasPostOnlyHooks) = - _removeHooks(permittedCallData.permittedCallHooks, preExecHook, postExecHook); - - if (shouldClearHasPreHooks) { - permittedCallData.hasPrePermittedCallHooks = false; - } - - if (shouldClearHasPostOnlyHooks) { - permittedCallData.hasPostOnlyPermittedCallHooks = false; - } - } - function _addHooks( HookGroup storage hooks, bytes4 selector, @@ -326,8 +275,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { address plugin, bytes32 manifestHash, bytes memory pluginInitData, - FunctionReference[] memory dependencies, - InjectedHook[] memory injectedHooks + FunctionReference[] memory dependencies ) internal { AccountStorage storage storage_ = _getAccountStorage(); @@ -382,7 +330,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { // Add installed plugin and selectors this plugin can call length = manifest.permittedExecutionSelectors.length; for (uint256 i = 0; i < length; ++i) { - _enableExecFromPlugin(manifest.permittedExecutionSelectors[i], plugin, storage_); + storage_.callPermitted[_getPermittedCallKey(plugin, manifest.permittedExecutionSelectors[i])] = true; } // Add the permitted external calls to the account. @@ -410,42 +358,6 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { } } - // Add injected hooks - length = injectedHooks.length; - // Manually set injected hooks array length - StoredInjectedHook[] storage injectedHooksArray = storage_.pluginData[plugin].injectedHooks; - assembly ("memory-safe") { - sstore(injectedHooksArray.slot, length) - } - for (uint256 i = 0; i < length; ++i) { - InjectedHook memory hook = injectedHooks[i]; - - // Check that the dependency is installed. This also blocks self-dependencies. - if (storage_.pluginData[hook.providingPlugin].manifestHash == bytes32(0)) { - revert MissingPluginDependency(hook.providingPlugin); - } - - injectedHooksArray[i] = StoredInjectedHook({ - providingPlugin: hook.providingPlugin, - selector: hook.selector, - preExecHookFunctionId: hook.injectedHooksInfo.preExecHookFunctionId, - isPostHookUsed: hook.injectedHooksInfo.isPostHookUsed, - postExecHookFunctionId: hook.injectedHooksInfo.postExecHookFunctionId - }); - - // Increment the dependent count for the plugin providing the hook. - storage_.pluginData[hook.providingPlugin].dependentCount += 1; - - _addPermittedCallHooks( - hook.selector, - plugin, - FunctionReferenceLib.pack(hook.providingPlugin, hook.injectedHooksInfo.preExecHookFunctionId), - hook.injectedHooksInfo.isPostHookUsed - ? FunctionReferenceLib.pack(hook.providingPlugin, hook.injectedHooksInfo.postExecHookFunctionId) - : FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE - ); - } - // Add user operation validation functions length = manifest.userOpValidationFunctions.length; for (uint256 i = 0; i < length; ++i) { @@ -518,27 +430,6 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { ); } - // Add pre and post permitted call hooks - length = manifest.permittedCallHooks.length; - for (uint256 i = 0; i < length; ++i) { - _addPermittedCallHooks( - manifest.permittedCallHooks[i].executionSelector, - plugin, - _resolveManifestFunction( - manifest.permittedCallHooks[i].preExecHook, - plugin, - dependencies, - ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY - ), - _resolveManifestFunction( - manifest.permittedCallHooks[i].postExecHook, - plugin, - dependencies, - ManifestAssociatedFunctionType.NONE - ) - ); - } - // Add new interface ids the plugin enabled for the account length = manifest.interfaceIds.length; for (uint256 i = 0; i < length; ++i) { @@ -558,23 +449,6 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { storage_.pluginData[plugin].canSpendNativeToken = true; } - // Call injected hooks' onHookApply after all setup, this is before calling plugin onInstall - length = injectedHooks.length; - for (uint256 i = 0; i < length; ++i) { - InjectedHook memory hook = injectedHooks[i]; - - /* solhint-disable no-empty-blocks */ - try IPlugin(hook.providingPlugin).onHookApply( - plugin, hook.injectedHooksInfo, injectedHooks[i].hookApplyData - ) {} catch (bytes memory revertReason) { - revert PluginApplyHookCallbackFailed(hook.providingPlugin, revertReason); - } - /* solhint-enable no-empty-blocks */ - - // zero out hookApplyData to reduce log cost - injectedHooks[i].hookApplyData = new bytes(0); - } - // Initialize the plugin storage for the account. // solhint-disable-next-line no-empty-blocks try IPlugin(plugin).onInstall(pluginInitData) {} @@ -582,14 +456,10 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { revert PluginInstallCallbackFailed(plugin, revertReason); } - emit PluginInstalled(plugin, manifestHash, dependencies, injectedHooks); + emit PluginInstalled(plugin, manifestHash, dependencies); } - function _uninstallPlugin( - UninstallPluginArgs memory args, - bytes calldata uninstallData, - bytes[] calldata hookUnapplyData - ) internal { + function _uninstallPlugin(UninstallPluginArgs memory args, bytes calldata uninstallData) internal { AccountStorage storage storage_ = _getAccountStorage(); // Check if the plugin exists. @@ -625,27 +495,6 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { // Remove components according to the manifest, in reverse order (by component type) of their installation. - // Remove pre and post permitted call hooks - length = args.manifest.permittedCallHooks.length; - for (uint256 i = 0; i < length; ++i) { - _removePermittedCallHooks( - args.manifest.permittedCallHooks[i].executionSelector, - args.plugin, - _resolveManifestFunction( - args.manifest.permittedCallHooks[i].preExecHook, - args.plugin, - dependencies, - ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY - ), - _resolveManifestFunction( - args.manifest.permittedCallHooks[i].postExecHook, - args.plugin, - dependencies, - ManifestAssociatedFunctionType.NONE - ) - ); - } - // Remove pre and post execution function hooks length = args.manifest.executionHooks.length; for (uint256 i = 0; i < length; ++i) { @@ -735,29 +584,11 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { } } - // Remove injected hooks - length = pluginData.injectedHooks.length; - for (uint256 i = 0; i < length; ++i) { - StoredInjectedHook memory hook = pluginData.injectedHooks[i]; - - // Decrement the dependent count for the plugin providing the hook. - storage_.pluginData[hook.providingPlugin].dependentCount -= 1; - - _removePermittedCallHooks( - hook.selector, - args.plugin, - FunctionReferenceLib.pack(hook.providingPlugin, hook.preExecHookFunctionId), - hook.isPostHookUsed - ? FunctionReferenceLib.pack(hook.providingPlugin, hook.postExecHookFunctionId) - : FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE - ); - } - // Remove permitted account execution function call permissions length = args.manifest.permittedExecutionSelectors.length; for (uint256 i = 0; i < length; ++i) { - storage_.permittedCalls[_getPermittedCallKey(args.plugin, args.manifest.permittedExecutionSelectors[i])] - .callPermitted = false; + storage_.callPermitted[_getPermittedCallKey(args.plugin, args.manifest.permittedExecutionSelectors[i])] + = false; } // Remove installed execution function @@ -772,36 +603,8 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { storage_.supportedInterfaces[args.manifest.interfaceIds[i]] -= 1; } - // Call onHookUnapply on all injected hooks - bool callbacksSucceeded = true; - length = pluginData.injectedHooks.length; - bool hasUnapplyHookData = hookUnapplyData.length != 0; - if (hasUnapplyHookData && hookUnapplyData.length != length) { - revert ArrayLengthMismatch(); - } - for (uint256 i = 0; i < length; ++i) { - StoredInjectedHook memory hook = pluginData.injectedHooks[i]; - - /* solhint-disable no-empty-blocks */ - try IPlugin(hook.providingPlugin).onHookUnapply{gas: args.callbackGasLimit}( - args.plugin, - InjectedHooksInfo({ - preExecHookFunctionId: hook.preExecHookFunctionId, - isPostHookUsed: hook.isPostHookUsed, - postExecHookFunctionId: hook.postExecHookFunctionId - }), - hasUnapplyHookData ? hookUnapplyData[i] : bytes("") - ) {} catch (bytes memory revertReason) { - if (!args.forceUninstall) { - revert PluginHookUnapplyCallbackFailed(hook.providingPlugin, revertReason); - } - callbacksSucceeded = false; - emit PluginIgnoredHookUnapplyCallbackFailure(args.plugin, hook.providingPlugin); - } - /* solhint-enable no-empty-blocks */ - } - // Clear the plugin storage for the account. + bool callbacksSucceeded = true; // solhint-disable-next-line no-empty-blocks try IPlugin(args.plugin).onUninstall{gas: args.callbackGasLimit}(uninstallData) {} catch (bytes memory revertReason) { diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index 23746605..a6d8a190 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -53,12 +53,11 @@ contract UpgradeableModularAccount is // plugin manifest has changed. If empty, uses the default behavior of // calling the plugin to get its current manifest. bytes serializedManifest; - // If true, will complete the uninstall even if `onUninstall` or - // `onHookUnapply` callbacks revert. Available as an escape hatch if a - // plugin is blocking uninstall. + // If true, will complete the uninstall even if `onUninstall` callbacks revert. Available as an escape + // hatch if a plugin is blocking uninstall. bool forceUninstall; // Maximum amount of gas allowed for each uninstall callback function - // (`onUninstall` and `onHookUnapply`), or zero to set no limit. Should + // (`onUninstall`), or zero to set no limit. Should // typically be used with `forceUninstall` to remove plugins that are // preventing uninstallation by consuming all remaining gas. uint256 callbackGasLimit; @@ -107,12 +106,9 @@ contract UpgradeableModularAccount is } FunctionReference[] memory emptyDependencies = new FunctionReference[](0); - InjectedHook[] memory emptyInjectedHooks = new InjectedHook[](0); for (uint256 i = 0; i < length; ++i) { - _installPlugin( - plugins[i], manifestHashes[i], pluginInstallDatas[i], emptyDependencies, emptyInjectedHooks - ); + _installPlugin(plugins[i], manifestHashes[i], pluginInstallDatas[i], emptyDependencies); } emit ModularAccountInitialized(_ENTRY_POINT); @@ -219,9 +215,8 @@ contract UpgradeableModularAccount is bytes24 permittedCallKey = _getPermittedCallKey(msg.sender, selector); AccountStorage storage storage_ = _getAccountStorage(); - PermittedCallData storage permittedCallData = storage_.permittedCalls[permittedCallKey]; - if (!permittedCallData.callPermitted) { + if (!storage_.callPermitted[permittedCallKey]) { revert ExecFromPluginNotPermitted(msg.sender, selector); } @@ -232,7 +227,7 @@ contract UpgradeableModularAccount is address execFunctionPlugin = selectorData.plugin; (FunctionReference[][] memory postHooksToRun, bytes[] memory postHookArgs) = - _doPrePermittedCallHooksAndPreExecHooks(selectorData, permittedCallData, callBuffer); + _doPreExecHooks(selectorData, callBuffer); if (execFunctionPlugin == address(0)) { revert UnrecognizedFunction(selector); @@ -303,16 +298,12 @@ contract UpgradeableModularAccount is revert ExecFromPluginExternalNotPermitted(callingPlugin, target, value, data); } - // Run any pre permitted call hooks specific to this caller and the `executeFromPluginExternal` selector, - // then run any pre-exec hooks associated with the `executeFromPluginExternal` selector. - PermittedCallData storage permittedCallData = storage_.permittedCalls[_getPermittedCallKey( - callingPlugin, IPluginExecutor.executeFromPluginExternal.selector - )]; + // Run any pre exec hooks for the `executeFromPluginExternal` selector SelectorData storage selectorData = storage_.selectorData[IPluginExecutor.executeFromPluginExternal.selector]; (FunctionReference[][] memory postHooksToRun, bytes[] memory postHookArgs) = - _doPrePermittedCallHooksAndPreExecHooks(selectorData, permittedCallData, ""); + _doPreExecHooks(selectorData, ""); // Perform the external call bytes memory returnData = _exec(target, value, data); @@ -327,21 +318,18 @@ contract UpgradeableModularAccount is address plugin, bytes32 manifestHash, bytes calldata pluginInitData, - FunctionReference[] calldata dependencies, - InjectedHook[] calldata injectedHooks + FunctionReference[] calldata dependencies ) external override { (FunctionReference[][] memory postExecHooks, bytes[] memory postHookArgs) = _preNativeFunction(); - _installPlugin(plugin, manifestHash, pluginInitData, dependencies, injectedHooks); + _installPlugin(plugin, manifestHash, pluginInitData, dependencies); _postNativeFunction(postExecHooks, postHookArgs); } /// @inheritdoc IPluginManager - function uninstallPlugin( - address plugin, - bytes calldata config, - bytes calldata pluginUninstallData, - bytes[] calldata hookUnapplyData - ) external override { + function uninstallPlugin(address plugin, bytes calldata config, bytes calldata pluginUninstallData) + external + override + { (FunctionReference[][] memory postExecHooks, bytes[] memory postHookArgs) = _preNativeFunction(); UninstallPluginArgs memory args; @@ -364,7 +352,7 @@ contract UpgradeableModularAccount is args.callbackGasLimit = type(uint256).max; } - _uninstallPlugin(args, pluginUninstallData, hookUnapplyData); + _uninstallPlugin(args, pluginUninstallData); _postNativeFunction(postExecHooks, postHookArgs); } @@ -581,135 +569,29 @@ contract UpgradeableModularAccount is postHooksToRun = new FunctionReference[][](postHooksToRunLength); postHookArgs = new bytes[](postHooksToRunLength); - uint256 currentIndex = 0; + // If there are no pre exec hooks, this will short-circuit in the length check on `preExecHooks`. + _cacheAssociatedPostHooks(preExecHooks, selectorData.executionHooks, postHooksToRun); if (hasPostOnlyExecHooks) { // If we have post-only hooks, we allocate an single FunctionReference[] for them, and one element // in the args for their empty `bytes` argument. We put this into the first element of the post // hooks in order to have it run last. - postHooksToRun[0] = + postHooksToRun[postHooksToRunLength - 1] = CastLib.toFunctionReferenceArray(selectorData.executionHooks.postOnlyHooks.getAll()); - unchecked { - ++currentIndex; - } } - // If there are no pre exec hooks, this will short-circuit in the length check on `preExecHooks`. - _cacheAssociatedPostHooks(preExecHooks, selectorData.executionHooks, postHooksToRun, currentIndex); - // Run all pre-exec hooks and capture their outputs. - _doPreHooks(preExecHooks, callBuffer, postHooksToRun, postHookArgs, currentIndex); - } - - /// @dev Executes pre-permitted call hooks and pre-exec hooks, and returns the post-exec hooks to run and - /// their associated args. - function _doPrePermittedCallHooksAndPreExecHooks( - SelectorData storage selectorData, - PermittedCallData storage permittedCallData, - bytes memory callBuffer - ) internal returns (FunctionReference[][] memory postHooksToRun, bytes[] memory postHookArgs) { - FunctionReference[] memory prePermittedCallHooks; - FunctionReference[] memory preExecHooks; - - bool hasPrePermittedCallHooks = permittedCallData.hasPrePermittedCallHooks; - bool hasPostOnlyPermittedCallHooks = permittedCallData.hasPostOnlyPermittedCallHooks; - - bool hasPreExecHooks = selectorData.hasPreExecHooks; - bool hasPostOnlyExecHooks = selectorData.hasPostOnlyExecHooks; - - // If we have any type of pre hooks, we need to allocate memory for them to perform their call. - if (callBuffer.length == 0 && (hasPrePermittedCallHooks || hasPreExecHooks)) { - callBuffer = _allocateRuntimeCallBuffer(msg.data); - } - - if (hasPrePermittedCallHooks) { - prePermittedCallHooks = - CastLib.toFunctionReferenceArray(permittedCallData.permittedCallHooks.preHooks.getAll()); - } - - if (hasPreExecHooks) { - preExecHooks = CastLib.toFunctionReferenceArray(selectorData.executionHooks.preHooks.getAll()); - } - - // Allocate memory for the post hooks and post hook args. - // If we have post-only hooks, we allocate an extra FunctionReference[] for them, and one extra element in - // the args for their empty `bytes` argument. - uint256 postHooksToRunLength = prePermittedCallHooks.length + preExecHooks.length - + (hasPostOnlyPermittedCallHooks ? 1 : 0) + (hasPostOnlyExecHooks ? 1 : 0); - postHooksToRun = new FunctionReference[][](postHooksToRunLength); - postHookArgs = new bytes[](postHooksToRunLength); - - // The order we want to fill the post exec hooks is: - // 1. Post-only permitted call hooks - // 2. Associated post hooks for pre-permitted call hooks - // 3. Post-only exec hooks - // 4. Associated post hooks for pre-exec hooks - - uint256 associatedPermittedCallHooksIndex = 0; - - if (hasPostOnlyPermittedCallHooks) { - // If we have post-only hooks, we allocate an single FunctionReference[] for them, and one element in - // the args for their empty `bytes` argument. We put this into the first element of the post hooks in - // order to have it run last. - postHooksToRun[associatedPermittedCallHooksIndex] = - CastLib.toFunctionReferenceArray(permittedCallData.permittedCallHooks.postOnlyHooks.getAll()); - unchecked { - ++associatedPermittedCallHooksIndex; - } - } - - // Cache post-permitted-call hooks in memory - _cacheAssociatedPostHooks( - prePermittedCallHooks, - permittedCallData.permittedCallHooks, - postHooksToRun, - associatedPermittedCallHooksIndex - ); - // The exec hooks start after the permitted call hooks - uint256 associatedExecHooksIndex; - unchecked { - associatedExecHooksIndex = associatedPermittedCallHooksIndex + prePermittedCallHooks.length; - } - - if (hasPostOnlyExecHooks) { - // If we have post-only hooks, we allocate an single FunctionReference[] for them, and one element in - // the args for their empty `bytes` argument. We put this into the first element of the post hooks in - // order to have it run last. - postHooksToRun[associatedExecHooksIndex] = - CastLib.toFunctionReferenceArray(selectorData.executionHooks.postOnlyHooks.getAll()); - unchecked { - ++associatedExecHooksIndex; - } - } - - // Cache post-exec hooks in memory - _cacheAssociatedPostHooks( - preExecHooks, selectorData.executionHooks, postHooksToRun, associatedExecHooksIndex - ); - - // Run the permitted call hooks - _doPreHooks( - prePermittedCallHooks, callBuffer, postHooksToRun, postHookArgs, associatedPermittedCallHooksIndex - ); - - // Run the pre-exec hooks - _doPreHooks(preExecHooks, callBuffer, postHooksToRun, postHookArgs, associatedExecHooksIndex); + _doPreHooks(preExecHooks, callBuffer, postHooksToRun, postHookArgs); } /// @dev Execute all pre hooks provided, using the call buffer if provided. - /// Outputs are captured into the `hookReturnData` array, in increasing index starting at `startingIndex`. + /// Outputs are captured into the `hookReturnData` array, in increasing index starting at 0. /// The `postHooks` array is used to determine whether or not to capture the return data. - /// NOTE: The caller must ensure that: - /// - `postHooks` is allocated, and `startingIndex + preHooks.length` does not exceed the array bounds of - /// `postHooks`. - /// - `hookReturnData` is allocated, and `startingIndex + preHooks.length` does not exceed the array bounds of - /// `hookReturnData`. function _doPreHooks( FunctionReference[] memory preHooks, bytes memory callBuffer, FunctionReference[][] memory postHooks, // Only used to check if any post hooks exist. - bytes[] memory hookReturnData, - uint256 startingIndex // Where to start writing into hookReturnData + bytes[] memory hookReturnData ) internal { uint256 preExecHooksLength = preHooks.length; @@ -741,14 +623,9 @@ contract UpgradeableModularAccount is _executeRuntimePluginFunction(callBuffer, plugin, PreExecHookReverted.selector); - uint256 adjustedIndex; - unchecked { - adjustedIndex = startingIndex + i; - } - // Only collect the return data if there is at least one post-hook to consume it. - if (postHooks[adjustedIndex].length > 0) { - hookReturnData[adjustedIndex] = abi.decode(_collectReturnData(), (bytes)); + if (postHooks[i].length > 0) { + hookReturnData[i] = abi.decode(_collectReturnData(), (bytes)); } } } @@ -793,15 +670,11 @@ contract UpgradeableModularAccount is // solhint-disable-next-line no-empty-blocks function _authorizeUpgrade(address newImplementation) internal override {} - /// @dev Loads the associated post hooks for the given pre-exec hooks in the `postHooks` array, starting at - /// `startingIndex`. - /// NOTE: The caller must ensure that `postHooks` is allocated, and `startingIndex + preHooks.length` does not - // exceed the array bounds of `postHooks`. + /// @dev Loads the associated post hooks for the given pre-exec hooks in the `postHooks` array, starting at 0. function _cacheAssociatedPostHooks( FunctionReference[] memory preExecHooks, HookGroup storage hookGroup, - FunctionReference[][] memory postHooks, - uint256 startingIndex + FunctionReference[][] memory postHooks ) internal view { uint256 preExecHooksLength = preExecHooks.length; for (uint256 i = 0; i < preExecHooksLength; ++i) { @@ -809,8 +682,7 @@ contract UpgradeableModularAccount is // If the pre-exec hook has associated post hooks, cache them in the postHooks array. if (hookGroup.preHooks.flagsEnabled(CastLib.toSetValue(preExecHook), _PRE_EXEC_HOOK_HAS_POST_FLAG)) { - // We start writing into the postHooks array starting at the `startingIndex` and counting up. - postHooks[startingIndex + i] = + postHooks[i] = CastLib.toFunctionReferenceArray(hookGroup.associatedPostHooks[preExecHook].getAll()); } // In no-associated-post-hooks case, we're OK returning the default value, which is an array of length diff --git a/src/helpers/KnownSelectors.sol b/src/helpers/KnownSelectors.sol index fbc6032d..2298f5e3 100644 --- a/src/helpers/KnownSelectors.sol +++ b/src/helpers/KnownSelectors.sol @@ -43,7 +43,6 @@ library KnownSelectors { // check against IAccountLoupe methods || selector == IAccountLoupe.getExecutionFunctionConfig.selector || selector == IAccountLoupe.getExecutionHooks.selector - || selector == IAccountLoupe.getPermittedCallHooks.selector || selector == IAccountLoupe.getPreValidationHooks.selector || selector == IAccountLoupe.getInstalledPlugins.selector; } @@ -61,8 +60,7 @@ library KnownSelectors { || selector == IPlugin.userOpValidationFunction.selector || selector == IPlugin.preRuntimeValidationHook.selector || selector == IPlugin.runtimeValidationFunction.selector || selector == IPlugin.preExecutionHook.selector - || selector == IPlugin.postExecutionHook.selector || selector == IPlugin.onHookApply.selector - || selector == IPlugin.onHookUnapply.selector || selector == IPlugin.pluginManifest.selector + || selector == IPlugin.postExecutionHook.selector || selector == IPlugin.pluginManifest.selector || selector == IPlugin.pluginMetadata.selector; } } diff --git a/src/interfaces/IAccountLoupe.sol b/src/interfaces/IAccountLoupe.sol index 15c7adb0..7503d970 100644 --- a/src/interfaces/IAccountLoupe.sol +++ b/src/interfaces/IAccountLoupe.sol @@ -30,15 +30,6 @@ interface IAccountLoupe { /// @return The pre and post execution hooks for this selector function getExecutionHooks(bytes4 selector) external view returns (ExecutionHooks[] memory); - /// @notice Gets the pre and post permitted call hooks applied for a plugin calling this selector - /// @param callingPlugin The plugin that is calling the selector - /// @param selector The selector the plugin is calling - /// @return The pre and post permitted call hooks for this selector - function getPermittedCallHooks(address callingPlugin, bytes4 selector) - external - view - returns (ExecutionHooks[] memory); - /// @notice Gets the pre user op and runtime validation hooks associated with a selector /// @param selector The selector to get the hooks for /// @return preUserOpValidationHooks The pre user op validation hooks for this selector diff --git a/src/interfaces/IPlugin.sol b/src/interfaces/IPlugin.sol index f8f1acce..62b3831b 100644 --- a/src/interfaces/IPlugin.sol +++ b/src/interfaces/IPlugin.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.22; -import {IPluginManager} from "./IPluginManager.sol"; - import {UserOperation} from "../interfaces/erc4337/UserOperation.sol"; // Forge formatter will displace the first comment for the enum field out of the enum itself, @@ -76,7 +74,6 @@ struct PluginManifest { ManifestAssociatedFunction[] preUserOpValidationHooks; ManifestAssociatedFunction[] preRuntimeValidationHooks; ManifestExecutionHook[] executionHooks; - ManifestExecutionHook[] permittedCallHooks; } /// @dev A struct holding fields to describe the plugin in a purely view context. Intended for front end clients. @@ -172,28 +169,6 @@ interface IPlugin { /// @param preExecHookData The context returned by its associated pre execution hook. function postExecutionHook(uint8 functionId, bytes calldata preExecHookData) external; - /// @notice A hook that runs when a hook this plugin owns is installed onto another plugin - /// @dev Optional, use to implement any required setup logic - /// @param pluginAppliedOn The plugin that the hook is being applied on - /// @param injectedHooksInfo Contains pre/post exec hook information - /// @param data Any optional data for setup - function onHookApply( - address pluginAppliedOn, - IPluginManager.InjectedHooksInfo calldata injectedHooksInfo, - bytes calldata data - ) external; - - /// @notice A hook that runs when a hook this plugin owns is unapplied from another plugin - /// @dev Optional, use to implement any required unapplied logic - /// @param pluginAppliedOn The plugin that the hook was applied on - /// @param injectedHooksInfo Contains pre/post exec hook information - /// @param data Any optional data for the unapplied call - function onHookUnapply( - address pluginAppliedOn, - IPluginManager.InjectedHooksInfo calldata injectedHooksInfo, - bytes calldata data - ) external; - /// @notice Describe the contents and intended configuration of the plugin. /// @dev This manifest MUST stay constant over time. /// @return A manifest describing the contents and intended configuration of the plugin. diff --git a/src/interfaces/IPluginManager.sol b/src/interfaces/IPluginManager.sol index 512dce82..5afbb4f8 100644 --- a/src/interfaces/IPluginManager.sol +++ b/src/interfaces/IPluginManager.sol @@ -5,31 +5,7 @@ import {FunctionReference} from "../libraries/FunctionReferenceLib.sol"; /// @title Plugin Manager Interface interface IPluginManager { - /// @dev Pre/post exec hooks added by the user to limit the scope of a plugin. These hooks are injected at - /// plugin install time - struct InjectedHook { - // The plugin that provides the hook - address providingPlugin; - // Either a plugin-defined execution function, or the native function executeFromPluginExternal - bytes4 selector; - InjectedHooksInfo injectedHooksInfo; - bytes hookApplyData; - } - - struct InjectedHooksInfo { - uint8 preExecHookFunctionId; - bool isPostHookUsed; - uint8 postExecHookFunctionId; - } - - /// @dev Note that we strip hookApplyData from InjectedHooks in this event for gas savings - event PluginInstalled( - address indexed plugin, - bytes32 manifestHash, - FunctionReference[] dependencies, - InjectedHook[] injectedHooks - ); - + event PluginInstalled(address indexed plugin, bytes32 manifestHash, FunctionReference[] dependencies); event PluginUninstalled(address indexed plugin, bool indexed callbacksSucceeded); event PluginIgnoredHookUnapplyCallbackFailure(address indexed plugin, address indexed providingPlugin); event PluginIgnoredUninstallCallbackFailure(address indexed plugin); @@ -40,14 +16,11 @@ interface IPluginManager { /// @param pluginInitData Optional data to be decoded and used by the plugin to setup initial plugin data for /// the modular account. /// @param dependencies The dependencies of the plugin, as described in the manifest. - /// @param injectedHooks Optional hooks to be injected over permitted calls this plugin may make. Alchemy - /// Accounts only support injected permitted call hooks. function installPlugin( address plugin, bytes32 manifestHash, bytes calldata pluginInitData, - FunctionReference[] calldata dependencies, - InjectedHook[] calldata injectedHooks + FunctionReference[] calldata dependencies ) external; /// @notice Uninstall a plugin from the modular account. @@ -57,12 +30,5 @@ interface IPluginManager { /// guarantees. /// @param pluginUninstallData Optional data to be decoded and used by the plugin to clear plugin data for the /// modular account. - /// @param hookUnapplyData Optional data to be decoded and used by the plugin to clear injected hooks for the - /// modular account. - function uninstallPlugin( - address plugin, - bytes calldata config, - bytes calldata pluginUninstallData, - bytes[] calldata hookUnapplyData - ) external; + function uninstallPlugin(address plugin, bytes calldata config, bytes calldata pluginUninstallData) external; } diff --git a/src/libraries/AccountStorageV1.sol b/src/libraries/AccountStorageV1.sol index 0fccb444..fd2be094 100644 --- a/src/libraries/AccountStorageV1.sol +++ b/src/libraries/AccountStorageV1.sol @@ -21,8 +21,8 @@ contract AccountStorageV1 { // Execution functions and their associated functions mapping(bytes4 => SelectorData) selectorData; // bytes24 key = address(calling plugin) || bytes4(selector of execution function) - mapping(bytes24 => PermittedCallData) permittedCalls; - // key = address(calling plugin) || target address + mapping(bytes24 => bool) callPermitted; + // keys = address(calling plugin), target address mapping(IPlugin => mapping(address => PermittedExternalCallData)) permittedExternalCalls; // For ERC165 introspection, each count indicates support from account or an installed plugin // 0 indicate the account does not support the interface and all plugins that support this interface have @@ -39,31 +39,6 @@ contract AccountStorageV1 { FunctionReference[] dependencies; // Tracks the number of times this plugin has been used as a dependency function uint256 dependentCount; - StoredInjectedHook[] injectedHooks; - } - - /// @dev A version of IPluginManager.InjectedHook used to track injected hooks in storage. Omits the - /// hookApplyData field, which is not needed for storage, and flattens the struct. - struct StoredInjectedHook { - // The plugin that provides the hook - address providingPlugin; - // Either a plugin-defined execution function, or the native function executeFromPluginExternal - bytes4 selector; - // Contents of the InjectedHooksInfo struct - uint8 preExecHookFunctionId; - bool isPostHookUsed; - uint8 postExecHookFunctionId; - } - - /// @dev Represents data associated with a plugin's permission to use `executeFromPlugin` to interact with - /// another plugin installed on the account. - struct PermittedCallData { - bool callPermitted; - // Cached flags indicating whether or not this function has pre permitted call hooks and - // post-only permitted call hooks. - bool hasPrePermittedCallHooks; - bool hasPostOnlyPermittedCallHooks; - HookGroup permittedCallHooks; } /// @dev Represents data associated with a plugin's permission to use `executeFromPluginExternal` to interact diff --git a/src/plugins/BasePlugin.sol b/src/plugins/BasePlugin.sol index a5888e89..6f05e6f9 100644 --- a/src/plugins/BasePlugin.sol +++ b/src/plugins/BasePlugin.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8.22; import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; import {IPlugin, PluginManifest, PluginMetadata} from "../interfaces/IPlugin.sol"; -import {IPluginManager} from "../interfaces/IPluginManager.sol"; import {UserOperation} from "../interfaces/erc4337/UserOperation.sol"; /// @title Base contract for plugins @@ -154,32 +153,6 @@ abstract contract BasePlugin is ERC165, IPlugin { revert NotImplemented(); } - /// @notice A hook that runs when a hook this plugin owns is installed onto another plugin - /// @dev Optional, use to implement any required setup logic - /// @param pluginAppliedOn The plugin that the hook is being applied on - /// @param injectedHooksInfo Contains pre/post exec hook information - /// @param data Any optional data for setup - function onHookApply( - address pluginAppliedOn, - IPluginManager.InjectedHooksInfo calldata injectedHooksInfo, - bytes calldata data - ) external virtual { - (pluginAppliedOn, injectedHooksInfo, data); - } - - /// @notice A hook that runs when a hook this plugin owns is unapplied from another plugin - /// @dev Optional, use to implement any required unapplied logic - /// @param pluginAppliedOn The plugin that the hook was applied on - /// @param injectedHooksInfo Contains pre/post exec hook information - /// @param data Any optional data for the unapplied call - function onHookUnapply( - address pluginAppliedOn, - IPluginManager.InjectedHooksInfo calldata injectedHooksInfo, - bytes calldata data - ) external virtual { - (pluginAppliedOn, injectedHooksInfo, data); - } - /// @notice Describe the contents and intended configuration of the plugin. /// @dev This manifest MUST stay constant over time. /// @return A manifest describing the contents and intended configuration of the plugin. diff --git a/test/account/AccountExecHooks.t.sol b/test/account/AccountExecHooks.t.sol index 83056336..c7c694c1 100644 --- a/test/account/AccountExecHooks.t.sol +++ b/test/account/AccountExecHooks.t.sol @@ -9,7 +9,6 @@ import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.so import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; -import {IPluginManager} from "../../src/interfaces/IPluginManager.sol"; import {FunctionReference, FunctionReferenceLib} from "../../src/libraries/FunctionReferenceLib.sol"; import { IPlugin, @@ -47,12 +46,7 @@ contract UpgradeableModularAccountExecHooksTest is Test { PluginManifest public m1; PluginManifest public m2; - event PluginInstalled( - address indexed plugin, - bytes32 manifestHash, - FunctionReference[] dependencies, - IPluginManager.InjectedHook[] injectedHooks - ); + event PluginInstalled(address indexed plugin, bytes32 manifestHash, FunctionReference[] dependencies); event PluginUninstalled(address indexed plugin, bool indexed callbacksSucceeded); // emitted by MockPlugin event ReceivedCall(bytes msgData, uint256 msgValue); @@ -1258,16 +1252,13 @@ contract UpgradeableModularAccountExecHooksTest is Test { vm.expectEmit(true, true, true, true); emit ReceivedCall(abi.encodeCall(IPlugin.onInstall, (bytes(""))), 0); vm.expectEmit(true, true, true, true); - emit PluginInstalled( - address(mockPlugin1), manifestHash1, new FunctionReference[](0), new IPluginManager.InjectedHook[](0) - ); + emit PluginInstalled(address(mockPlugin1), manifestHash1, new FunctionReference[](0)); account1.installPlugin({ plugin: address(mockPlugin1), manifestHash: manifestHash1, pluginInitData: bytes(""), - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); return mockPlugin1; @@ -1294,16 +1285,13 @@ contract UpgradeableModularAccountExecHooksTest is Test { vm.expectEmit(true, true, true, true); emit ReceivedCall(abi.encodeCall(IPlugin.onInstall, (bytes(""))), 0); vm.expectEmit(true, true, true, true); - emit PluginInstalled( - address(mockPlugin2), manifestHash2, dependencies, new IPluginManager.InjectedHook[](0) - ); + emit PluginInstalled(address(mockPlugin2), manifestHash2, dependencies); account1.installPlugin({ plugin: address(mockPlugin2), manifestHash: manifestHash2, pluginInitData: bytes(""), - dependencies: dependencies, - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: dependencies }); } @@ -1313,6 +1301,6 @@ contract UpgradeableModularAccountExecHooksTest is Test { vm.expectEmit(true, true, true, true); emit PluginUninstalled(address(plugin), true); - account1.uninstallPlugin(address(plugin), bytes(""), bytes(""), new bytes[](0)); + account1.uninstallPlugin(address(plugin), bytes(""), bytes("")); } } diff --git a/test/account/AccountLoupe.t.sol b/test/account/AccountLoupe.t.sol index 9a48224c..8064af2e 100644 --- a/test/account/AccountLoupe.t.sol +++ b/test/account/AccountLoupe.t.sol @@ -57,13 +57,7 @@ contract AccountLoupeTest is Test { account1 = UpgradeableModularAccount(payable(factory.createAccount(0, owners))); bytes32 manifestHash = keccak256(abi.encode(comprehensivePlugin.pluginManifest())); - account1.installPlugin( - address(comprehensivePlugin), - manifestHash, - "", - new FunctionReference[](0), - new IPluginManager.InjectedHook[](0) - ); + account1.installPlugin(address(comprehensivePlugin), manifestHash, "", new FunctionReference[](0)); ownerUserOpValidation = FunctionReferenceLib.pack( address(multiOwnerPlugin), uint8(IMultiOwnerPlugin.FunctionId.USER_OP_VALIDATION_OWNER) @@ -183,34 +177,6 @@ contract AccountLoupeTest is Test { ); } - function test_pluginLoupe_getPermittedCallHooks() public { - IAccountLoupe.ExecutionHooks[] memory hooks = - account1.getPermittedCallHooks(address(comprehensivePlugin), comprehensivePlugin.foo.selector); - - assertEq(hooks.length, 2); - - _assertHookEq( - hooks[0], - FunctionReferenceLib.pack( - address(comprehensivePlugin), - uint8(ComprehensivePlugin.FunctionId.PRE_PERMITTED_CALL_EXECUTION_HOOK) - ), - FunctionReferenceLib.pack( - address(comprehensivePlugin), - uint8(ComprehensivePlugin.FunctionId.POST_PERMITTED_CALL_EXECUTION_HOOK) - ) - ); - - _assertHookEq( - hooks[1], - FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE, - FunctionReferenceLib.pack( - address(comprehensivePlugin), - uint8(ComprehensivePlugin.FunctionId.POST_PERMITTED_CALL_EXECUTION_HOOK) - ) - ); - } - function test_pluginLoupe_getHooks_multiple() public { // Add a third set of execution hooks to the account, and validate that it can return all hooks applied // over the function. @@ -232,29 +198,10 @@ contract AccountLoupeTest is Test { }) }); - mockPluginManifest.permittedCallHooks = new ManifestExecutionHook[](2); - // Copy over the same hooks from executionHooks. - mockPluginManifest.permittedCallHooks[0] = mockPluginManifest.executionHooks[0]; - mockPluginManifest.permittedCallHooks[1] = ManifestExecutionHook({ - executionSelector: ComprehensivePlugin.foo.selector, - preExecHook: ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: 1, - dependencyIndex: 0 - }), - postExecHook: ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: 1, - dependencyIndex: 0 - }) - }); - MockPlugin mockPlugin = new MockPlugin(mockPluginManifest); bytes32 manifestHash = keccak256(abi.encode(mockPlugin.pluginManifest())); - account1.installPlugin( - address(mockPlugin), manifestHash, "", new FunctionReference[](0), new IPluginManager.InjectedHook[](0) - ); + account1.installPlugin(address(mockPlugin), manifestHash, "", new FunctionReference[](0)); // Assert that the returned execution hooks are what is expected @@ -285,24 +232,6 @@ contract AccountLoupeTest is Test { address(comprehensivePlugin), uint8(ComprehensivePlugin.FunctionId.POST_EXECUTION_HOOK) ) ); - - // Assert that the returned permitted call hooks are what is expected - - hooks = account1.getPermittedCallHooks(address(mockPlugin), comprehensivePlugin.foo.selector); - - assertEq(hooks.length, 2); - - _assertHookEq( - hooks[0], - FunctionReferenceLib.pack(address(mockPlugin), uint8(1)), - FunctionReferenceLib.pack(address(mockPlugin), uint8(1)) - ); - - _assertHookEq( - hooks[1], - FunctionReferenceLib.pack(address(mockPlugin), uint8(0)), - FunctionReferenceLib.pack(address(mockPlugin), uint8(0)) - ); } function test_pluginLoupe_getPreValidationHooks() public { @@ -495,9 +424,7 @@ contract AccountLoupeTest is Test { MockPlugin mockPlugin = new MockPlugin(mockPluginManifest); bytes32 manifestHash = keccak256(abi.encode(mockPlugin.pluginManifest())); - account1.installPlugin( - address(mockPlugin), manifestHash, "", new FunctionReference[](0), new IPluginManager.InjectedHook[](0) - ); + account1.installPlugin(address(mockPlugin), manifestHash, "", new FunctionReference[](0)); // Assert that the returned execution hooks are what is expected diff --git a/test/account/AccountPermittedCallHooks.t.sol b/test/account/AccountPermittedCallHooks.t.sol deleted file mode 100644 index 1fc425b9..00000000 --- a/test/account/AccountPermittedCallHooks.t.sol +++ /dev/null @@ -1,871 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.22; - -import {Test} from "forge-std/Test.sol"; - -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; - -import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; -import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; -import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; -import {IPluginManager} from "../../src/interfaces/IPluginManager.sol"; -import {FunctionReference} from "../../src/libraries/FunctionReferenceLib.sol"; -import { - IPlugin, - ManifestExecutionHook, - PluginManifest, - ManifestFunction, - ManifestAssociatedFunctionType, - ManifestAssociatedFunction -} from "../../src/interfaces/IPlugin.sol"; - -import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; -import {MockPlugin} from "../mocks/MockPlugin.sol"; - -/// @dev Unlike execution hooks, permitted call hooks are scoped to the plugin that is executing the call -/// through `executeFromPlugin`. Therefore, different plugins cannot apply overlapping hooks to the same -/// plugin + selector combination. Overlapping hooks in this case can only originate from the same plugin, -/// which is unrealistic but possible. That's what we test here. -contract UpgradeableModularAccountPermittedCallHooksTest is Test { - using ECDSA for bytes32; - - IEntryPoint public entryPoint; - MultiOwnerPlugin public multiOwnerPlugin; - MultiOwnerMSCAFactory public factory; - MockPlugin public mockPlugin1; - bytes32 public manifestHash1; - - address public owner1; - uint256 public owner1Key; - UpgradeableModularAccount public account1; - - bytes4 internal constant _EXEC_SELECTOR = bytes4(uint32(1)); - uint8 internal constant _PRE_HOOK_FUNCTION_ID_1 = 1; - uint8 internal constant _POST_HOOK_FUNCTION_ID_2 = 2; - uint8 internal constant _PRE_HOOK_FUNCTION_ID_3 = 3; - uint8 internal constant _POST_HOOK_FUNCTION_ID_4 = 4; - - PluginManifest public m1; - - event PluginInstalled( - address indexed plugin, - bytes32 manifestHash, - FunctionReference[] dependencies, - IPluginManager.InjectedHook[] injectedHooks - ); - event PluginUninstalled(address indexed plugin, bool indexed callbacksSucceeded); - // emitted by MockPlugin - event ReceivedCall(bytes msgData, uint256 msgValue); - - function setUp() public { - entryPoint = IEntryPoint(address(new EntryPoint())); - multiOwnerPlugin = new MultiOwnerPlugin(); - - (owner1, owner1Key) = makeAddrAndKey("owner1"); - address impl = address(new UpgradeableModularAccount(IEntryPoint(address(entryPoint)))); - - factory = new MultiOwnerMSCAFactory( - address(this), - address(multiOwnerPlugin), - impl, - keccak256(abi.encode(multiOwnerPlugin.pluginManifest())), - entryPoint - ); - - address[] memory owners = new address[](1); - owners[0] = owner1; - account1 = UpgradeableModularAccount(payable(factory.createAccount(0, owners))); - vm.deal(address(account1), 100 ether); - - entryPoint.depositTo{value: 1 wei}(address(account1)); - - m1.executionFunctions.push(_EXEC_SELECTOR); - - m1.runtimeValidationFunctions.push( - ManifestAssociatedFunction({ - executionSelector: _EXEC_SELECTOR, - associatedFunction: ManifestFunction({ - functionType: ManifestAssociatedFunctionType.RUNTIME_VALIDATION_ALWAYS_ALLOW, - functionId: 0, - dependencyIndex: 0 - }) - }) - ); - - m1.permittedExecutionSelectors.push(_EXEC_SELECTOR); - } - - /// @dev Plugin hook pair(s): [1, null] - /// Expected execution: [1, null] - function test_prePermittedCallHook_install() public { - vm.startPrank(owner1); - - _installPlugin1WithHooks( - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _PRE_HOOK_FUNCTION_ID_1, - dependencyIndex: 0 - }), - ManifestFunction({functionType: ManifestAssociatedFunctionType.NONE, functionId: 0, dependencyIndex: 0}) - ); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [1, null] - /// Expected execution: [1, null] - function test_prePermittedCallHook_run() public { - test_prePermittedCallHook_install(); - - vm.startPrank(address(mockPlugin1)); - - vm.expectEmit(true, true, true, true); - emit ReceivedCall( - abi.encodeWithSelector( - IPlugin.preExecutionHook.selector, - _PRE_HOOK_FUNCTION_ID_1, - address(mockPlugin1), // caller - 0, // msg.value in call to account - abi.encodePacked(_EXEC_SELECTOR) - ), - 0 // msg value in call to plugin - ); - - account1.executeFromPlugin(abi.encodePacked(_EXEC_SELECTOR)); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [1, null] - /// Expected execution: [1, null] - function test_prePermittedCallHook_uninstall() public { - test_prePermittedCallHook_install(); - - vm.startPrank(owner1); - - _uninstallPlugin(mockPlugin1); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [1, 2] - /// Expected execution: [1, 2] - function test_permittedCallHookPair_install() public { - vm.startPrank(owner1); - - _installPlugin1WithHooks( - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _PRE_HOOK_FUNCTION_ID_1, - dependencyIndex: 0 - }), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _POST_HOOK_FUNCTION_ID_2, - dependencyIndex: 0 - }) - ); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [1, 2] - /// Expected execution: [1, 2] - function test_permittedCallHookPair_run() public { - test_permittedCallHookPair_install(); - - vm.startPrank(address(mockPlugin1)); - - vm.expectEmit(true, true, true, true); - // pre hook call - emit ReceivedCall( - abi.encodeWithSelector( - IPlugin.preExecutionHook.selector, - _PRE_HOOK_FUNCTION_ID_1, - address(mockPlugin1), // caller - 0, // msg.value in call to account - abi.encodePacked(_EXEC_SELECTOR) - ), - 0 // msg value in call to plugin - ); - vm.expectEmit(true, true, true, true); - // exec call - emit ReceivedCall(abi.encodePacked(_EXEC_SELECTOR), 0); - vm.expectEmit(true, true, true, true); - // post hook call - emit ReceivedCall( - abi.encodeCall( - IPlugin.postExecutionHook, (_POST_HOOK_FUNCTION_ID_2, abi.encode(_PRE_HOOK_FUNCTION_ID_1)) - ), - 0 // msg value in call to plugin - ); - - account1.executeFromPlugin(abi.encodePacked(_EXEC_SELECTOR)); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [1, 2] - /// Expected execution: [1, 2] - function test_permittedCallHookPair_uninstall() public { - test_permittedCallHookPair_install(); - - vm.startPrank(owner1); - - _uninstallPlugin(mockPlugin1); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [null, 2] - /// Expected execution: [null, 2] - function test_postOnlyPermittedCallHook_install() public { - vm.startPrank(owner1); - - _installPlugin1WithHooks( - ManifestFunction({functionType: ManifestAssociatedFunctionType.NONE, functionId: 0, dependencyIndex: 0}), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _POST_HOOK_FUNCTION_ID_2, - dependencyIndex: 0 - }) - ); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [null, 2] - /// Expected execution: [null, 2] - function test_postOnlyPermittedCallHook_run() public { - test_postOnlyPermittedCallHook_install(); - - vm.startPrank(address(mockPlugin1)); - - vm.expectEmit(true, true, true, true); - emit ReceivedCall( - abi.encodeCall(IPlugin.postExecutionHook, (_POST_HOOK_FUNCTION_ID_2, "")), - 0 // msg value in call to plugin - ); - - account1.executeFromPlugin(abi.encodePacked(_EXEC_SELECTOR)); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [null, 2] - /// Expected execution: [null, 2] - function test_postOnlyPermittedCallHook_uninstall() public { - test_postOnlyPermittedCallHook_install(); - - vm.startPrank(owner1); - - _uninstallPlugin(mockPlugin1); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [1, null], [1, null] - /// Expected execution: [1, null] - function test_overlappingPrePermittedCallHooks_install() public { - vm.startPrank(owner1); - - _installPlugin1WithHooks( - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _PRE_HOOK_FUNCTION_ID_1, - dependencyIndex: 0 - }), - ManifestFunction({functionType: ManifestAssociatedFunctionType.NONE, functionId: 0, dependencyIndex: 0}), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _PRE_HOOK_FUNCTION_ID_1, - dependencyIndex: 0 - }), - ManifestFunction({functionType: ManifestAssociatedFunctionType.NONE, functionId: 0, dependencyIndex: 0}) - ); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [1, null], [1, null] - /// Expected execution: [1, null] - function test_overlappingPrePermittedCallHooks_run() public { - test_overlappingPrePermittedCallHooks_install(); - - vm.startPrank(address(mockPlugin1)); - - // Expect the pre hook to be called just once. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.preExecutionHook.selector, - _PRE_HOOK_FUNCTION_ID_1, - address(mockPlugin1), // caller - 0, // msg.value in call to account - abi.encodePacked(_EXEC_SELECTOR) - ), - 1 - ); - - account1.executeFromPlugin(abi.encodePacked(_EXEC_SELECTOR)); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [1, null], [1, null] - /// Expected execution: [1, null] - function test_overlappingPrePermittedCallHooks_uninstall() public { - test_overlappingPrePermittedCallHooks_install(); - - vm.startPrank(owner1); - - _uninstallPlugin(mockPlugin1); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [1, 2], [1, 2] - /// Expected execution: [1, 2] - function test_overlappingPermittedCallHookPairs_install() public { - vm.startPrank(owner1); - - _installPlugin1WithHooks( - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _PRE_HOOK_FUNCTION_ID_1, - dependencyIndex: 0 - }), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _POST_HOOK_FUNCTION_ID_2, - dependencyIndex: 0 - }), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _PRE_HOOK_FUNCTION_ID_1, - dependencyIndex: 0 - }), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _POST_HOOK_FUNCTION_ID_2, - dependencyIndex: 0 - }) - ); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [1, 2], [1, 2] - /// Expected execution: [1, 2] - function test_overlappingPermittedCallHookPairs_run() public { - test_overlappingPermittedCallHookPairs_install(); - - vm.startPrank(address(mockPlugin1)); - - // Expect the pre hook to be called just once. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.preExecutionHook.selector, - _PRE_HOOK_FUNCTION_ID_1, - address(mockPlugin1), // caller - 0, // msg.value in call to account - abi.encodePacked(_EXEC_SELECTOR) - ), - 1 - ); - - // Expect the post hook to be called just once, with the expected data. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_2, - abi.encode(_PRE_HOOK_FUNCTION_ID_1) // preExecHookData - ), - 1 - ); - - account1.executeFromPlugin(abi.encodePacked(_EXEC_SELECTOR)); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [1, 2], [1, 2] - /// Expected execution: [1, 2] - function test_overlappingPermittedCallHookPairs_uninstall() public { - test_overlappingPermittedCallHookPairs_install(); - - vm.startPrank(owner1); - - _uninstallPlugin(mockPlugin1); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [1, 2], [3, 2] - /// Expected execution: [1, 2], [3, 2] - function test_overlappingPermittedCallHookPairsOnPost_install() public { - vm.startPrank(owner1); - - _installPlugin1WithHooks( - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _PRE_HOOK_FUNCTION_ID_1, - dependencyIndex: 0 - }), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _POST_HOOK_FUNCTION_ID_2, - dependencyIndex: 0 - }), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _PRE_HOOK_FUNCTION_ID_3, - dependencyIndex: 0 - }), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _POST_HOOK_FUNCTION_ID_2, - dependencyIndex: 0 - }) - ); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [1, 2], [3, 2] - /// Expected execution: [1, 2], [3, 2] - function test_overlappingPermittedCallHookPairsOnPost_run() public { - test_overlappingPermittedCallHookPairsOnPost_install(); - - vm.startPrank(address(mockPlugin1)); - - // Expect each pre hook to be called once. - - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.preExecutionHook.selector, - _PRE_HOOK_FUNCTION_ID_3, - address(mockPlugin1), // caller - 0, // msg.value in call to account - abi.encodePacked(_EXEC_SELECTOR) - ), - 1 - ); - - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.preExecutionHook.selector, - _PRE_HOOK_FUNCTION_ID_1, - address(mockPlugin1), // caller - 0, // msg.value in call to account - abi.encodePacked(_EXEC_SELECTOR) - ), - 1 - ); - - // Expect the post hook to be called twice, with the expected data. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_2, - abi.encode(_PRE_HOOK_FUNCTION_ID_1) // preExecHookData - ), - 1 - ); - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_2, - abi.encode(_PRE_HOOK_FUNCTION_ID_3) // preExecHookData - ), - 1 - ); - - account1.executeFromPlugin(abi.encodePacked(_EXEC_SELECTOR)); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [1, 2], [3, 2] - /// Expected execution: [1, 2], [3, 2] - function test_overlappingPermittedCallHookPairsOnPost_uninstall() public { - test_overlappingPermittedCallHookPairsOnPost_install(); - - vm.startPrank(owner1); - - _uninstallPlugin(mockPlugin1); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [1, 2], [1, 4] - /// Expected execution: [1, 2], [1, 4] - function test_overlappingPermittedCallHookPairsOnPre_install() public { - vm.startPrank(owner1); - - _installPlugin1WithHooks( - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _PRE_HOOK_FUNCTION_ID_1, - dependencyIndex: 0 - }), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _POST_HOOK_FUNCTION_ID_2, - dependencyIndex: 0 - }), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _PRE_HOOK_FUNCTION_ID_1, - dependencyIndex: 0 - }), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _POST_HOOK_FUNCTION_ID_4, - dependencyIndex: 0 - }) - ); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [1, 2], [1, 4] - /// Expected execution: [1, 2], [null, 4] - function test_overlappingPermittedCallHookPairsOnPre_run() public { - test_overlappingPermittedCallHookPairsOnPre_install(); - - vm.startPrank(address(mockPlugin1)); - - // Expect the pre hook to be called twice, each passing data over to their respective post hooks. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.preExecutionHook.selector, - _PRE_HOOK_FUNCTION_ID_1, - address(mockPlugin1), // caller - 0, // msg.value in call to account - abi.encodePacked(_EXEC_SELECTOR) - ), - 1 - ); - - // Expect each post hook to be called once, with the expected data. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_2, - abi.encode(_PRE_HOOK_FUNCTION_ID_1) // preExecHookData - ), - 1 - ); - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_4, - abi.encode(_PRE_HOOK_FUNCTION_ID_1) // preExecHookData - ), - 1 - ); - - account1.executeFromPlugin(abi.encodePacked(_EXEC_SELECTOR)); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [1, 2], [1, 4] - /// Expected execution: [1, 2], [1, 4] - function test_overlappingPermittedCallHookPairsOnPre_uninstall() public { - test_overlappingPermittedCallHookPairsOnPre_install(); - - vm.startPrank(owner1); - - _uninstallPlugin(mockPlugin1); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [1, 2], [1, null] - /// Expected execution: [1, 2] - function test_overlappingPermittedCallHookPairsOnPreWithNullPost_install() public { - vm.startPrank(owner1); - - _installPlugin1WithHooks( - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _PRE_HOOK_FUNCTION_ID_1, - dependencyIndex: 0 - }), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _POST_HOOK_FUNCTION_ID_2, - dependencyIndex: 0 - }), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _PRE_HOOK_FUNCTION_ID_1, - dependencyIndex: 0 - }), - ManifestFunction({functionType: ManifestAssociatedFunctionType.NONE, functionId: 0, dependencyIndex: 0}) - ); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [1, 2], [1, null] - /// Expected execution: [1, 2] - function test_overlappingPermittedCallHookPairsOnPreWithNullPost_run() public { - test_overlappingPermittedCallHookPairsOnPreWithNullPost_install(); - - vm.startPrank(address(mockPlugin1)); - - // Expect the pre hook to be called just once. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.preExecutionHook.selector, - _PRE_HOOK_FUNCTION_ID_1, - address(mockPlugin1), // caller - 0, // msg.value in call to account - abi.encodePacked(_EXEC_SELECTOR) - ), - 1 - ); - - // Expect the post hook to be called just once, with the expected data. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_2, - abi.encode(_PRE_HOOK_FUNCTION_ID_1) // preExecHookData - ), - 1 - ); - - account1.executeFromPlugin(abi.encodePacked(_EXEC_SELECTOR)); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [1, 2], [1, null] - /// Expected execution: [1, 2] - function test_overlappingPermittedCallHookPairsOnPreWithNullPost_uninstall() public { - test_overlappingPermittedCallHookPairsOnPreWithNullPost_install(); - - vm.startPrank(owner1); - - _uninstallPlugin(mockPlugin1); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [1, 2], [null, 2] - /// Expected execution: [1, 2], [null, 2] - function test_overlappingPermittedCallHookPairsOnPreWithNullPre_install() public { - vm.startPrank(owner1); - - _installPlugin1WithHooks( - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _PRE_HOOK_FUNCTION_ID_1, - dependencyIndex: 0 - }), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _POST_HOOK_FUNCTION_ID_2, - dependencyIndex: 0 - }), - ManifestFunction({functionType: ManifestAssociatedFunctionType.NONE, functionId: 0, dependencyIndex: 0}), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _POST_HOOK_FUNCTION_ID_2, - dependencyIndex: 0 - }) - ); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [1, 2], [null, 2] - /// Expected execution: [1, 2], [null, 2] - function test_overlappingPermittedCallHookPairsOnPreWithNullPre_run() public { - test_overlappingPermittedCallHookPairsOnPreWithNullPre_install(); - - vm.startPrank(address(mockPlugin1)); - - // Expect the pre hook to be called just once. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.preExecutionHook.selector, - _PRE_HOOK_FUNCTION_ID_1, - address(mockPlugin1), // caller - 0, // msg.value in call to account - abi.encodePacked(_EXEC_SELECTOR) - ), - 1 - ); - - // Expect the post hook to be called twice, with the expected data. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_2, - abi.encode(_PRE_HOOK_FUNCTION_ID_1) // preExecHookData - ), - 1 - ); - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_2, - "" // preExecHookData - ), - 1 - ); - - account1.executeFromPlugin(abi.encodePacked(_EXEC_SELECTOR)); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [1, 2], [null, 2] - /// Expected execution: [1, 2], [null, 2] - function test_overlappingPermittedCallHookPairsOnPreWithNullPre_uninstall() public { - test_overlappingPermittedCallHookPairsOnPreWithNullPre_install(); - - vm.startPrank(owner1); - - _uninstallPlugin(mockPlugin1); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [null, 2], [null, 2] - /// Expected execution: [null, 2] - function test_overlappingPostPermittedCallHooks_install() public { - vm.startPrank(owner1); - - _installPlugin1WithHooks( - ManifestFunction({functionType: ManifestAssociatedFunctionType.NONE, functionId: 0, dependencyIndex: 0}), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _POST_HOOK_FUNCTION_ID_2, - dependencyIndex: 0 - }), - ManifestFunction({functionType: ManifestAssociatedFunctionType.NONE, functionId: 0, dependencyIndex: 0}), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _POST_HOOK_FUNCTION_ID_2, - dependencyIndex: 0 - }) - ); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [null, 2], [null, 2] - /// Expected execution: [null, 2] - function test_overlappingPostPermittedCallHooks_run() public { - test_overlappingPostPermittedCallHooks_install(); - - vm.startPrank(address(mockPlugin1)); - - // Expect the post hook to be called just once. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_2, - "" // preExecHookData - ), - 1 - ); - - account1.executeFromPlugin(abi.encodePacked(_EXEC_SELECTOR)); - - vm.stopPrank(); - } - - /// @dev Plugin hook pair(s): [null, 2], [null, 2] - /// Expected execution: [null, 2] - function test_overlappingPostPermittedCallHooks_uninstall() public { - test_overlappingPostPermittedCallHooks_install(); - - vm.startPrank(owner1); - - _uninstallPlugin(mockPlugin1); - - vm.stopPrank(); - } - - function _installPlugin1WithHooks(ManifestFunction memory preHook1, ManifestFunction memory postHook1) - internal - { - m1.permittedCallHooks.push(ManifestExecutionHook(_EXEC_SELECTOR, preHook1, postHook1)); - mockPlugin1 = new MockPlugin(m1); - manifestHash1 = keccak256(abi.encode(mockPlugin1.pluginManifest())); - - vm.expectEmit(true, true, true, true); - emit ReceivedCall(abi.encodeCall(IPlugin.onInstall, (bytes(""))), 0); - vm.expectEmit(true, true, true, true); - emit PluginInstalled( - address(mockPlugin1), manifestHash1, new FunctionReference[](0), new IPluginManager.InjectedHook[](0) - ); - - account1.installPlugin({ - plugin: address(mockPlugin1), - manifestHash: manifestHash1, - pluginInitData: bytes(""), - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) - }); - } - - function _installPlugin1WithHooks( - ManifestFunction memory preHook1, - ManifestFunction memory postHook1, - ManifestFunction memory preHook2, - ManifestFunction memory postHook2 - ) internal { - m1.permittedCallHooks.push(ManifestExecutionHook(_EXEC_SELECTOR, preHook1, postHook1)); - m1.permittedCallHooks.push(ManifestExecutionHook(_EXEC_SELECTOR, preHook2, postHook2)); - mockPlugin1 = new MockPlugin(m1); - manifestHash1 = keccak256(abi.encode(mockPlugin1.pluginManifest())); - - vm.expectEmit(true, true, true, true); - emit ReceivedCall(abi.encodeCall(IPlugin.onInstall, (bytes(""))), 0); - vm.expectEmit(true, true, true, true); - emit PluginInstalled( - address(mockPlugin1), manifestHash1, new FunctionReference[](0), new IPluginManager.InjectedHook[](0) - ); - - account1.installPlugin({ - plugin: address(mockPlugin1), - manifestHash: manifestHash1, - pluginInitData: bytes(""), - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) - }); - } - - function _uninstallPlugin(MockPlugin plugin) internal { - vm.expectEmit(true, true, true, true); - emit ReceivedCall(abi.encodeCall(IPlugin.onUninstall, (bytes(""))), 0); - vm.expectEmit(true, true, true, true); - emit PluginUninstalled(address(plugin), true); - - account1.uninstallPlugin(address(plugin), bytes(""), bytes(""), new bytes[](0)); - } -} diff --git a/test/account/AccountPreValidationHooks.t.sol b/test/account/AccountPreValidationHooks.t.sol index f59aa92f..8836c00b 100644 --- a/test/account/AccountPreValidationHooks.t.sol +++ b/test/account/AccountPreValidationHooks.t.sol @@ -11,7 +11,6 @@ import {IMultiOwnerPlugin} from "../../src/plugins/owner/IMultiOwnerPlugin.sol"; import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../src/interfaces/erc4337/UserOperation.sol"; -import {IPluginManager} from "../../src/interfaces/IPluginManager.sol"; import {FunctionReference, FunctionReferenceLib} from "../../src/libraries/FunctionReferenceLib.sol"; import { IPlugin, @@ -48,12 +47,7 @@ contract UpgradeableModularAccountPreValidationHooksTest is Test { uint256 public constant CALL_GAS_LIMIT = 70000; uint256 public constant VERIFICATION_GAS_LIMIT = 1000000; - event PluginInstalled( - address indexed plugin, - bytes32 manifestHash, - FunctionReference[] dependencies, - IPluginManager.InjectedHook[] injectedHooks - ); + event PluginInstalled(address indexed plugin, bytes32 manifestHash, FunctionReference[] dependencies); event PluginUninstalled(address indexed plugin, bool indexed callbacksSucceeded); function setUp() public { @@ -620,16 +614,13 @@ contract UpgradeableModularAccountPreValidationHooksTest is Test { vm.expectCall(address(mockPlugin1), abi.encodeCall(IPlugin.onInstall, ("")), 1); vm.expectEmit(true, true, true, true); - emit PluginInstalled( - address(mockPlugin1), manifestHash1, new FunctionReference[](0), new IPluginManager.InjectedHook[](0) - ); + emit PluginInstalled(address(mockPlugin1), manifestHash1, new FunctionReference[](0)); account1.installPlugin({ plugin: address(mockPlugin1), manifestHash: manifestHash1, pluginInitData: bytes(""), - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); return mockPlugin1; @@ -650,16 +641,13 @@ contract UpgradeableModularAccountPreValidationHooksTest is Test { vm.expectCall(address(mockPlugin2), abi.encodeCall(IPlugin.onInstall, ("")), 1); vm.expectEmit(true, true, true, true); - emit PluginInstalled( - address(mockPlugin2), manifestHash2, dependencies, new IPluginManager.InjectedHook[](0) - ); + emit PluginInstalled(address(mockPlugin2), manifestHash2, dependencies); account1.installPlugin({ plugin: address(mockPlugin2), manifestHash: manifestHash2, pluginInitData: bytes(""), - dependencies: dependencies, - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: dependencies }); } @@ -688,16 +676,13 @@ contract UpgradeableModularAccountPreValidationHooksTest is Test { vm.expectCall(address(mockPlugin1), abi.encodeCall(IPlugin.onInstall, ("")), 1); vm.expectEmit(true, true, true, true); - emit PluginInstalled( - address(mockPlugin1), manifestHash1, dependencies, new IPluginManager.InjectedHook[](0) - ); + emit PluginInstalled(address(mockPlugin1), manifestHash1, dependencies); account1.installPlugin({ plugin: address(mockPlugin1), manifestHash: manifestHash1, pluginInitData: bytes(""), - dependencies: dependencies, - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: dependencies }); } @@ -716,16 +701,13 @@ contract UpgradeableModularAccountPreValidationHooksTest is Test { vm.expectCall(address(mockPlugin2), abi.encodeCall(IPlugin.onInstall, ("")), 1); vm.expectEmit(true, true, true, true); - emit PluginInstalled( - address(mockPlugin2), manifestHash2, dependencies, new IPluginManager.InjectedHook[](0) - ); + emit PluginInstalled(address(mockPlugin2), manifestHash2, dependencies); account1.installPlugin({ plugin: address(mockPlugin2), manifestHash: manifestHash2, pluginInitData: bytes(""), - dependencies: dependencies, - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: dependencies }); } @@ -734,6 +716,6 @@ contract UpgradeableModularAccountPreValidationHooksTest is Test { vm.expectEmit(true, true, true, true); emit PluginUninstalled(address(plugin), true); - account1.uninstallPlugin(address(plugin), bytes(""), bytes(""), new bytes[](0)); + account1.uninstallPlugin(address(plugin), bytes(""), bytes("")); } } diff --git a/test/account/AccountReturnData.t.sol b/test/account/AccountReturnData.t.sol index db57d6b7..d93834c3 100644 --- a/test/account/AccountReturnData.t.sol +++ b/test/account/AccountReturnData.t.sol @@ -5,7 +5,6 @@ import {Test} from "forge-std/Test.sol"; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; -import {IPluginManager} from "../../src/interfaces/IPluginManager.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; @@ -60,8 +59,7 @@ contract AccountReturnDataTest is Test { plugin: address(resultCreatorPlugin), manifestHash: resultCreatorManifestHash, pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); // Add the result consumer plugin to the account bytes32 resultConsumerManifestHash = keccak256(abi.encode(resultConsumerPlugin.pluginManifest())); @@ -69,8 +67,7 @@ contract AccountReturnDataTest is Test { plugin: address(resultConsumerPlugin), manifestHash: resultConsumerManifestHash, pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); } diff --git a/test/account/ExecuteFromPluginPermissions.t.sol b/test/account/ExecuteFromPluginPermissions.t.sol index dec83ade..328e9d95 100644 --- a/test/account/ExecuteFromPluginPermissions.t.sol +++ b/test/account/ExecuteFromPluginPermissions.t.sol @@ -6,7 +6,6 @@ import {Test, console} from "forge-std/Test.sol"; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; import {IPlugin} from "../../src/interfaces/IPlugin.sol"; -import {IPluginManager} from "../../src/interfaces/IPluginManager.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; @@ -21,9 +20,7 @@ import { EFPCallerPlugin, EFPCallerPluginAnyExternal, EFPCallerPluginAnyExternalCanSpendNativeToken, - EFPExecutionHookPlugin, - EFPExternalPermittedCallHookPlugin, - EFPPermittedCallHookPlugin + EFPExecutionHookPlugin } from "../mocks/plugins/ExecFromPluginPermissionsMocks.sol"; contract ExecuteFromPluginPermissionsTest is Test { @@ -40,8 +37,6 @@ contract ExecuteFromPluginPermissionsTest is Test { EFPCallerPlugin public efpCallerPlugin; EFPCallerPluginAnyExternal public efpCallerPluginAnyExternal; EFPCallerPluginAnyExternalCanSpendNativeToken public efpCallerPluginAnyExternalCanSpendNativeToken; - EFPPermittedCallHookPlugin public efpPermittedCallHookPlugin; - EFPExternalPermittedCallHookPlugin public efpExternalPermittedCallHookPlugin; EFPExecutionHookPlugin public efpExecutionHookPlugin; function setUp() public { @@ -68,8 +63,6 @@ contract ExecuteFromPluginPermissionsTest is Test { efpCallerPlugin = new EFPCallerPlugin(); efpCallerPluginAnyExternal = new EFPCallerPluginAnyExternal(); efpCallerPluginAnyExternalCanSpendNativeToken = new EFPCallerPluginAnyExternalCanSpendNativeToken(); - efpPermittedCallHookPlugin = new EFPPermittedCallHookPlugin(); - efpExternalPermittedCallHookPlugin = new EFPExternalPermittedCallHookPlugin(); efpExecutionHookPlugin = new EFPExecutionHookPlugin(); // Create an account with "this" as the owner, so we can execute along the runtime path with regular @@ -84,8 +77,7 @@ contract ExecuteFromPluginPermissionsTest is Test { plugin: address(resultCreatorPlugin), manifestHash: resultCreatorManifestHash, pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); // Add the EFP caller plugin to the account bytes32 efpCallerManifestHash = keccak256(abi.encode(efpCallerPlugin.pluginManifest())); @@ -93,8 +85,7 @@ contract ExecuteFromPluginPermissionsTest is Test { plugin: address(efpCallerPlugin), manifestHash: efpCallerManifestHash, pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); // Add the EFP caller plugin with any external permissions to the account @@ -104,8 +95,7 @@ contract ExecuteFromPluginPermissionsTest is Test { plugin: address(efpCallerPluginAnyExternal), manifestHash: efpCallerAnyExternalManifestHash, pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); // Add the EFP caller plugin with any external permissions and native token spend permission to the account @@ -115,30 +105,7 @@ contract ExecuteFromPluginPermissionsTest is Test { plugin: address(efpCallerPluginAnyExternalCanSpendNativeToken), manifestHash: efpCallerAnyExternalCanSpendNativeTokenManifestHash, pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) - }); - - // Add the EFP caller plugin with permitted call hooks to the account - bytes32 efpPermittedCallHookManifestHash = - keccak256(abi.encode(efpPermittedCallHookPlugin.pluginManifest())); - account.installPlugin({ - plugin: address(efpPermittedCallHookPlugin), - manifestHash: efpPermittedCallHookManifestHash, - pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) - }); - - // Add the EFP caller plugin with an external permitted call hook to the account - bytes32 efpExternalPermittedCallHookManifestHash = - keccak256(abi.encode(efpExternalPermittedCallHookPlugin.pluginManifest())); - account.installPlugin({ - plugin: address(efpExternalPermittedCallHookPlugin), - manifestHash: efpExternalPermittedCallHookManifestHash, - pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); // Add the EFP caller plugin with execution hooks to the account @@ -147,8 +114,7 @@ contract ExecuteFromPluginPermissionsTest is Test { plugin: address(efpExecutionHookPlugin), manifestHash: efpExecutionHookPluginManifestHash, pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); } @@ -389,35 +355,6 @@ contract ExecuteFromPluginPermissionsTest is Test { assertEq(address(recipient).balance, 2 ether); } - function test_executeFromPlugin_PermittedCallHooks() public { - assertFalse(efpPermittedCallHookPlugin.preExecHookCalled()); - assertFalse(efpPermittedCallHookPlugin.postExecHookCalled()); - - bytes memory result = EFPPermittedCallHookPlugin(address(account)).performEFPCall(); - - bytes32 actual = abi.decode(result, (bytes32)); - - assertEq(actual, keccak256("bar")); - - assertTrue(efpPermittedCallHookPlugin.preExecHookCalled()); - assertTrue(efpPermittedCallHookPlugin.postExecHookCalled()); - } - - function test_executeFromPluginExternal_PermittedCallHooks() public { - counter1.setNumber(17); - - assertFalse(efpExternalPermittedCallHookPlugin.preExecHookCalled()); - assertFalse(efpExternalPermittedCallHookPlugin.postExecHookCalled()); - - EFPExternalPermittedCallHookPlugin(address(account)).performIncrement(); - - assertTrue(efpExternalPermittedCallHookPlugin.preExecHookCalled()); - assertTrue(efpExternalPermittedCallHookPlugin.postExecHookCalled()); - - uint256 retrievedNumber = counter1.number(); - assertEq(retrievedNumber, 18); - } - function test_executeFromPlugin_ExecutionHooks() public { // Expect the pre hook to be called just once. vm.expectCall( diff --git a/test/account/ManifestValidity.t.sol b/test/account/ManifestValidity.t.sol index 3d564baf..f28d5095 100644 --- a/test/account/ManifestValidity.t.sol +++ b/test/account/ManifestValidity.t.sol @@ -5,7 +5,6 @@ import {Test} from "forge-std/Test.sol"; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; -import {IPluginManager} from "../../src/interfaces/IPluginManager.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {PluginManagerInternals} from "../../src/account/PluginManagerInternals.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; @@ -63,8 +62,7 @@ contract ManifestValidityTest is Test { plugin: address(plugin), manifestHash: manifestHash, pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); } @@ -81,8 +79,7 @@ contract ManifestValidityTest is Test { plugin: address(plugin), manifestHash: manifestHash, pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); } @@ -99,8 +96,7 @@ contract ManifestValidityTest is Test { plugin: address(plugin), manifestHash: manifestHash, pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); } @@ -115,8 +111,7 @@ contract ManifestValidityTest is Test { plugin: address(plugin), manifestHash: manifestHash, pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); } @@ -131,8 +126,7 @@ contract ManifestValidityTest is Test { plugin: address(plugin), manifestHash: manifestHash, pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); } @@ -148,8 +142,7 @@ contract ManifestValidityTest is Test { plugin: address(plugin), manifestHash: manifestHash, pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); } @@ -165,8 +158,7 @@ contract ManifestValidityTest is Test { plugin: address(plugin), manifestHash: manifestHash, pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); } @@ -181,8 +173,7 @@ contract ManifestValidityTest is Test { plugin: address(plugin), manifestHash: manifestHash, pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); } } diff --git a/test/account/UpgradeableModularAccount.t.sol b/test/account/UpgradeableModularAccount.t.sol index 7823f09d..c985f895 100644 --- a/test/account/UpgradeableModularAccount.t.sol +++ b/test/account/UpgradeableModularAccount.t.sol @@ -51,11 +51,6 @@ contract UpgradeableModularAccountTest is Test { address public ethRecipient; Counter public counter; PluginManifest public manifest; - IPluginManager.InjectedHooksInfo public injectedHooksInfo = IPluginManager.InjectedHooksInfo({ - preExecHookFunctionId: 2, - isPostHookUsed: true, - postExecHookFunctionId: 3 - }); uint256 public constant CALL_GAS_LIMIT = 500000; uint256 public constant VERIFICATION_GAS_LIMIT = 2000000; @@ -481,8 +476,7 @@ contract UpgradeableModularAccountTest is Test { plugin: address(plugin), manifestHash: manifestHash, pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); vm.stopPrank(); diff --git a/test/account/UpgradeableModularAccountPluginManager.t.sol b/test/account/UpgradeableModularAccountPluginManager.t.sol index 9bc337f4..49f70a4b 100644 --- a/test/account/UpgradeableModularAccountPluginManager.t.sol +++ b/test/account/UpgradeableModularAccountPluginManager.t.sol @@ -17,7 +17,6 @@ import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; import {PluginManifest} from "../../src/interfaces/IPlugin.sol"; import {IAccountLoupe} from "../../src/interfaces/IAccountLoupe.sol"; import {IPluginManager} from "../../src/interfaces/IPluginManager.sol"; -import {IPluginExecutor} from "../../src/interfaces/IPluginExecutor.sol"; import {IStandardExecutor} from "../../src/interfaces/IStandardExecutor.sol"; import {Call} from "../../src/interfaces/IStandardExecutor.sol"; import {FunctionReference, FunctionReferenceLib} from "../../src/libraries/FunctionReferenceLib.sol"; @@ -57,21 +56,11 @@ contract UpgradeableModularAccountPluginManagerTest is Test { address public ethRecipient; Counter public counter; PluginManifest public manifest; - IPluginManager.InjectedHooksInfo public injectedHooksInfo = IPluginManager.InjectedHooksInfo({ - preExecHookFunctionId: 2, - isPostHookUsed: true, - postExecHookFunctionId: 3 - }); uint256 public constant CALL_GAS_LIMIT = 500000; uint256 public constant VERIFICATION_GAS_LIMIT = 2000000; - event PluginInstalled( - address indexed plugin, - bytes32 manifestHash, - FunctionReference[] dependencies, - IPluginManager.InjectedHook[] injectedHooks - ); + event PluginInstalled(address indexed plugin, bytes32 manifestHash, FunctionReference[] dependencies); event PluginUninstalled(address indexed plugin, bool indexed callbacksSucceeded); event PluginIgnoredHookUnapplyCallbackFailure(address indexed plugin, address indexed providingPlugin); event PluginIgnoredUninstallCallbackFailure(address indexed plugin); @@ -130,33 +119,24 @@ contract UpgradeableModularAccountPluginManagerTest is Test { sessionKeys[0] = owner1; vm.expectEmit(true, true, true, true); - emit PluginInstalled( - address(sessionKeyPlugin), manifestHash, dependencies, new IPluginManager.InjectedHook[](0) - ); + emit PluginInstalled(address(sessionKeyPlugin), manifestHash, dependencies); IPluginManager(account2).installPlugin({ plugin: address(sessionKeyPlugin), manifestHash: manifestHash, pluginInitData: abi.encode( sessionKeys, new bytes32[](sessionKeys.length), new bytes[][](sessionKeys.length) ), - dependencies: dependencies, - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: dependencies }); manifestHash = keccak256(abi.encode(tokenReceiverPlugin.pluginManifest())); vm.expectEmit(true, true, true, true); - emit PluginInstalled( - address(tokenReceiverPlugin), - manifestHash, - new FunctionReference[](0), - new IPluginManager.InjectedHook[](0) - ); + emit PluginInstalled(address(tokenReceiverPlugin), manifestHash, new FunctionReference[](0)); IPluginManager(account2).installPlugin({ plugin: address(tokenReceiverPlugin), manifestHash: manifestHash, pluginInitData: abi.encode(uint48(1 days)), - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); address[] memory plugins = IAccountLoupe(account2).getInstalledPlugins(); @@ -182,8 +162,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { plugin: address(mockPluginWithBadPermittedExec), manifestHash: manifestHash, pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); } @@ -195,8 +174,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { plugin: address(tokenReceiverPlugin), manifestHash: bytes32(0), pluginInitData: abi.encode(uint48(1 days)), - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); } @@ -211,8 +189,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { plugin: address(badPlugin), manifestHash: bytes32(0), pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); } @@ -224,8 +201,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { plugin: address(tokenReceiverPlugin), manifestHash: manifestHash, pluginInitData: abi.encode(uint48(1 days)), - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); vm.expectRevert( @@ -237,8 +213,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { plugin: address(tokenReceiverPlugin), manifestHash: manifestHash, pluginInitData: abi.encode(uint48(1 days)), - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); } @@ -260,8 +235,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { plugin: address(mockPluginBad), manifestHash: manifestHashBad, pluginInitData: bytes(""), - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); } @@ -283,8 +257,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { plugin: address(mockPluginBad), manifestHash: manifestHashBad, pluginInitData: bytes(""), - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); } @@ -302,8 +275,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { plugin: address(mockPluginBad), manifestHash: manifestHashBad, pluginInitData: bytes(""), - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); } @@ -326,8 +298,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { plugin: address(mockPluginBad), manifestHash: manifestHashBad, pluginInitData: bytes(""), - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); } @@ -349,8 +320,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { plugin: address(newPlugin), manifestHash: manifestHash, pluginInitData: "", - dependencies: dependencies, - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: dependencies }); // Add invalid function reference that points to a plugin that is not yet installed (and also is not the @@ -364,8 +334,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { plugin: address(newPlugin), manifestHash: manifestHash, pluginInitData: "", - dependencies: dependencies, - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: dependencies }); } @@ -378,18 +347,12 @@ contract UpgradeableModularAccountPluginManagerTest is Test { plugin: address(plugin), manifestHash: manifestHash, pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); vm.expectEmit(true, true, true, true); emit PluginUninstalled(address(plugin), true); - IPluginManager(account2).uninstallPlugin({ - plugin: address(plugin), - config: "", - pluginUninstallData: "", - hookUnapplyData: new bytes[](0) - }); + IPluginManager(account2).uninstallPlugin({plugin: address(plugin), config: "", pluginUninstallData: ""}); address[] memory plugins = IAccountLoupe(account2).getInstalledPlugins(); assertEq(plugins.length, 1); assertEq(plugins[0], address(multiOwnerPlugin)); @@ -405,8 +368,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { plugin: address(plugin), manifestHash: manifestHash, pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); bytes memory config = abi.encode( @@ -418,12 +380,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { ); vm.expectEmit(true, true, true, true); emit PluginUninstalled(address(plugin), true); - IPluginManager(account2).uninstallPlugin({ - plugin: address(plugin), - config: config, - pluginUninstallData: "", - hookUnapplyData: new bytes[](0) - }); + IPluginManager(account2).uninstallPlugin({plugin: address(plugin), config: config, pluginUninstallData: ""}); address[] memory plugins = IAccountLoupe(account2).getInstalledPlugins(); assertEq(plugins.length, 1); assertEq(plugins[0], address(multiOwnerPlugin)); @@ -439,8 +396,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { plugin: address(plugin), manifestHash: manifestHash, pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); // Attempt to uninstall with a blank manifest @@ -454,12 +410,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { ); vm.expectRevert(abi.encodeWithSelector(PluginManagerInternals.InvalidPluginManifest.selector)); - IPluginManager(account2).uninstallPlugin({ - plugin: address(plugin), - config: config, - pluginUninstallData: "", - hookUnapplyData: new bytes[](0) - }); + IPluginManager(account2).uninstallPlugin({plugin: address(plugin), config: config, pluginUninstallData: ""}); // The forceUninstall flag doesn't let you succeed if your manifest is // wrong. @@ -472,12 +423,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { ); vm.expectRevert(abi.encodeWithSelector(PluginManagerInternals.InvalidPluginManifest.selector)); - IPluginManager(account2).uninstallPlugin({ - plugin: address(plugin), - config: config, - pluginUninstallData: "", - hookUnapplyData: new bytes[](0) - }); + IPluginManager(account2).uninstallPlugin({plugin: address(plugin), config: config, pluginUninstallData: ""}); address[] memory plugins = IAccountLoupe(account2).getInstalledPlugins(); assertEq(plugins.length, 2); @@ -495,8 +441,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { plugin: address(plugin), manifestHash: manifestHash, pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); plugin.changeManifest(); @@ -507,12 +452,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { // Default uninstall should fail because the manifest has changed. vm.expectRevert(abi.encodeWithSelector(PluginManagerInternals.InvalidPluginManifest.selector)); - IPluginManager(account2).uninstallPlugin({ - plugin: address(plugin), - config: "", - pluginUninstallData: "", - hookUnapplyData: new bytes[](0) - }); + IPluginManager(account2).uninstallPlugin({plugin: address(plugin), config: "", pluginUninstallData: ""}); // Uninstall should succeed with original manifest hash passed in bytes memory config = abi.encode( @@ -524,12 +464,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { ); vm.expectEmit(true, true, true, true); emit PluginUninstalled(address(plugin), true); - IPluginManager(account2).uninstallPlugin({ - plugin: address(plugin), - config: config, - pluginUninstallData: "", - hookUnapplyData: new bytes[](0) - }); + IPluginManager(account2).uninstallPlugin({plugin: address(plugin), config: config, pluginUninstallData: ""}); address[] memory plugins = IAccountLoupe(account2).getInstalledPlugins(); assertEq(plugins.length, 1); assertEq(plugins[0], address(multiOwnerPlugin)); @@ -556,46 +491,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { abi.encodeWithSelector(UninstallErrorsPlugin.IntentionalUninstallError.selector) ) ); - IPluginManager(account2).uninstallPlugin({ - plugin: plugin, - config: "", - pluginUninstallData: "", - hookUnapplyData: new bytes[](0) - }); - - bytes memory config = abi.encode( - UpgradeableModularAccount.UninstallPluginConfig({ - serializedManifest: "", - forceUninstall: true, - callbackGasLimit: 0 - }) - ); - vm.expectEmit(true, true, true, true); - emit PluginUninstalled(plugin, false); - IPluginManager(account2).uninstallPlugin({ - plugin: plugin, - config: config, - pluginUninstallData: "", - hookUnapplyData: new bytes[](0) - }); - } - - function test_forceOnHookUnapply() external { - (address plugin, address hooksPlugin) = _installPluginWithHookUnapplyErrors(false); - - vm.expectRevert( - abi.encodeWithSelector( - PluginManagerInternals.PluginHookUnapplyCallbackFailed.selector, - hooksPlugin, - abi.encodeWithSelector(UninstallErrorsPlugin.IntentionalUninstallError.selector) - ) - ); - IPluginManager(account2).uninstallPlugin({ - plugin: plugin, - config: "", - pluginUninstallData: "", - hookUnapplyData: new bytes[](0) - }); + IPluginManager(account2).uninstallPlugin({plugin: plugin, config: "", pluginUninstallData: ""}); bytes memory config = abi.encode( UpgradeableModularAccount.UninstallPluginConfig({ @@ -606,12 +502,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { ); vm.expectEmit(true, true, true, true); emit PluginUninstalled(plugin, false); - IPluginManager(account2).uninstallPlugin({ - plugin: plugin, - config: config, - pluginUninstallData: "", - hookUnapplyData: new bytes[](0) - }); + IPluginManager(account2).uninstallPlugin({plugin: plugin, config: config, pluginUninstallData: ""}); } function test_onUninstallGasLimit() external { @@ -623,8 +514,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { IPluginManager(account2).uninstallPlugin{gas: 100_000}({ plugin: plugin, config: "", - pluginUninstallData: "", - hookUnapplyData: new bytes[](0) + pluginUninstallData: "" }); // Just `forceUninstall` isn't enough. @@ -639,8 +529,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { IPluginManager(account2).uninstallPlugin{gas: 100_000}({ plugin: plugin, config: config, - pluginUninstallData: "", - hookUnapplyData: new bytes[](0) + pluginUninstallData: "" }); config = abi.encode( @@ -657,236 +546,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { IPluginManager(account2).uninstallPlugin{gas: 100_000}({ plugin: plugin, config: config, - pluginUninstallData: "", - hookUnapplyData: new bytes[](0) - }); - } - - function test_onHookUnapplyGasLimit() external { - (address plugin, address hooksPlugin) = _installPluginWithHookUnapplyErrors(true); - - vm.expectRevert( - abi.encodeWithSelector( - PluginManagerInternals.PluginHookUnapplyCallbackFailed.selector, hooksPlugin, "" - ) - ); - IPluginManager(account2).uninstallPlugin{gas: 100_000}({ - plugin: plugin, - config: "", - pluginUninstallData: "", - hookUnapplyData: new bytes[](0) - }); - - bytes memory config = abi.encode( - UpgradeableModularAccount.UninstallPluginConfig({ - serializedManifest: "", - forceUninstall: true, - callbackGasLimit: 3000 - }) - ); - vm.expectEmit(true, true, true, true); - emit PluginIgnoredHookUnapplyCallbackFailure(plugin, hooksPlugin); - vm.expectEmit(true, true, true, true); - emit PluginUninstalled(plugin, false); - IPluginManager(account2).uninstallPlugin{gas: 100_000}({ - plugin: plugin, - config: config, - pluginUninstallData: "", - hookUnapplyData: new bytes[](0) - }); - } - - function test_injectHooks() external { - (, MockPlugin newPlugin,) = _installWithInjectHooks(); - - // order of emitting events: pre hook is run, exec function is run, post hook is run - vm.expectEmit(true, true, true, true); - emit ReceivedCall( - abi.encodeWithSelector( - IPlugin.preExecutionHook.selector, - injectedHooksInfo.preExecHookFunctionId, - address(newPlugin), // caller - 0, // msg.value in call to account - abi.encodeCall( - account2.executeFromPluginExternal, - (address(counter), 0, abi.encodePacked(counter.increment.selector)) - ) - ), - 0 // msg value in call to plugin - ); - vm.expectEmit(true, true, true, true); - emit ReceivedCall( - abi.encodeCall( - IPlugin.postExecutionHook, - (injectedHooksInfo.postExecHookFunctionId, abi.encode(injectedHooksInfo.preExecHookFunctionId)) - ), - 0 // msg value in call to plugin - ); - vm.prank(address(newPlugin)); - account2.executeFromPluginExternal(address(counter), 0, abi.encodePacked(counter.increment.selector)); - } - - function test_injectHooksApplyGoodCalldata() external { - MockPlugin hooksPlugin = _installPluginWithExecHooks(); - - MockPlugin newPlugin = new MockPlugin(manifest); - - bytes32 manifestHash = keccak256(abi.encode(newPlugin.pluginManifest())); - - IPluginManager.InjectedHook[] memory hooks = new IPluginManager.InjectedHook[](1); - bytes memory onApplyData = abi.encode(keccak256("randomdata")); - hooks[0] = IPluginManager.InjectedHook( - address(hooksPlugin), IPluginExecutor.executeFromPluginExternal.selector, injectedHooksInfo, "" - ); - - vm.expectEmit(true, true, true, true); - emit ReceivedCall( - abi.encodeCall(IPlugin.onHookApply, (address(newPlugin), injectedHooksInfo, onApplyData)), 0 - ); - vm.expectEmit(true, true, true, true); - emit PluginInstalled(address(newPlugin), manifestHash, new FunctionReference[](0), hooks); - - // set the apply data after as the event emits an InjectedHook object after stripping hookApplyData out - hooks[0].hookApplyData = onApplyData; - - vm.prank(owner2); - IPluginManager(account2).installPlugin({ - plugin: address(newPlugin), - manifestHash: manifestHash, - pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: hooks - }); - } - - function test_injectHooksMissingPlugin() external { - // hooks plugin not installed - MockPlugin hooksPlugin = MockPlugin(payable(address(1))); - - MockPlugin newPlugin = new MockPlugin(manifest); - - bytes32 manifestHash = keccak256(abi.encode(newPlugin.pluginManifest())); - - IPluginManager.InjectedHook[] memory hooks = new IPluginManager.InjectedHook[](1); - hooks[0] = IPluginManager.InjectedHook( - address(hooksPlugin), IPluginExecutor.executeFromPluginExternal.selector, injectedHooksInfo, "" - ); - - vm.expectRevert( - abi.encodeWithSelector(PluginManagerInternals.MissingPluginDependency.selector, address(hooksPlugin)) - ); - vm.prank(owner2); - IPluginManager(account2).installPlugin({ - plugin: address(newPlugin), - manifestHash: manifestHash, - pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: hooks - }); - } - - function test_injectHooksMissingDependency() external { - vm.startPrank(owner2); - - MockPlugin newPlugin = new MockPlugin(manifest); - bytes32 manifestHash = keccak256(abi.encode(newPlugin.pluginManifest())); - - // Add invalid injected hook that points to the plugin being installed, rather than an existing dependency. - IPluginManager.InjectedHook[] memory hooks = new IPluginManager.InjectedHook[](1); - hooks[0] = IPluginManager.InjectedHook( - address(newPlugin), IPluginExecutor.executeFromPluginExternal.selector, injectedHooksInfo, "" - ); - vm.expectRevert( - abi.encodeWithSelector(PluginManagerInternals.MissingPluginDependency.selector, address(newPlugin)) - ); - IPluginManager(account2).installPlugin({ - plugin: address(newPlugin), - manifestHash: manifestHash, - pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: hooks - }); - - // Add invalid injected hook that points to a plugin that is not yet installed (and also is not the one - // currently being installed). - MockPlugin newPlugin2 = new MockPlugin(manifest); - hooks[0] = IPluginManager.InjectedHook( - address(newPlugin2), IPluginExecutor.executeFromPluginExternal.selector, injectedHooksInfo, "" - ); - vm.expectRevert( - abi.encodeWithSelector(PluginManagerInternals.MissingPluginDependency.selector, address(newPlugin2)) - ); - IPluginManager(account2).installPlugin({ - plugin: address(newPlugin), - manifestHash: manifestHash, - pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: hooks - }); - } - - function test_injectHooksUninstall() external { - (, MockPlugin newPlugin,) = _installWithInjectHooks(); - - vm.expectEmit(true, true, true, true); - emit PluginUninstalled(address(newPlugin), true); - vm.prank(owner2); - IPluginManager(account2).uninstallPlugin({ - plugin: address(newPlugin), - config: "", - pluginUninstallData: "", - hookUnapplyData: new bytes[](0) - }); - } - - function test_injectHooksBadUninstallDependency() external { - (MockPlugin hooksPlugin,,) = _installWithInjectHooks(); - - vm.prank(owner2); - vm.expectRevert( - abi.encodeWithSelector(PluginManagerInternals.PluginDependencyViolation.selector, address(hooksPlugin)) - ); - IPluginManager(account2).uninstallPlugin({ - plugin: address(hooksPlugin), - config: "", - pluginUninstallData: "", - hookUnapplyData: new bytes[](0) - }); - } - - function test_injectHooksUnapplyGoodCalldata() external { - (, MockPlugin newPlugin,) = _installWithInjectHooks(); - - bytes[] memory injectedHooksDatas = new bytes[](1); - injectedHooksDatas[0] = abi.encode(keccak256("randomdata")); - - vm.expectEmit(true, true, true, true); - emit ReceivedCall( - abi.encodeCall(IPlugin.onHookUnapply, (address(newPlugin), injectedHooksInfo, injectedHooksDatas[0])), - 0 - ); - vm.prank(owner2); - IPluginManager(account2).uninstallPlugin({ - plugin: address(newPlugin), - config: "", - pluginUninstallData: "", - hookUnapplyData: injectedHooksDatas - }); - } - - function test_injectHooksUnapplyBadCalldata() external { - (, MockPlugin newPlugin,) = _installWithInjectHooks(); - - // length != installed hooks length - bytes[] memory injectedHooksDatas = new bytes[](2); - - vm.expectRevert(PluginManagerInternals.ArrayLengthMismatch.selector); - vm.prank(owner2); - IPluginManager(account2).uninstallPlugin({ - plugin: address(newPlugin), - config: "", - pluginUninstallData: "", - hookUnapplyData: injectedHooksDatas + pluginUninstallData: "" }); } @@ -905,22 +565,19 @@ contract UpgradeableModularAccountPluginManagerTest is Test { calls[0] = Call({ target: address(account2), value: 0, - data: abi.encodeCall(IPluginManager.uninstallPlugin, (address(multiOwnerPlugin), "", "", new bytes[](0))) + data: abi.encodeCall(IPluginManager.uninstallPlugin, (address(multiOwnerPlugin), "", "")) }); calls[1] = Call({ target: address(account2), value: 0, data: abi.encodeCall( - IPluginManager.installPlugin, - (address(plugin), manifestHash, "", new FunctionReference[](0), new IPluginManager.InjectedHook[](0)) + IPluginManager.installPlugin, (address(plugin), manifestHash, "", new FunctionReference[](0)) ) }); vm.expectEmit(true, true, true, true); emit PluginUninstalled(address(multiOwnerPlugin), true); vm.expectEmit(true, true, true, true); - emit PluginInstalled( - address(plugin), manifestHash, new FunctionReference[](0), new IPluginManager.InjectedHook[](0) - ); + emit PluginInstalled(address(plugin), manifestHash, new FunctionReference[](0)); IStandardExecutor(account2).executeBatch(calls); } @@ -937,7 +594,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { calls[0] = Call({ target: address(account2), value: 0, - data: abi.encodeCall(IPluginManager.uninstallPlugin, (address(multiOwnerPlugin), "", "", new bytes[](0))) + data: abi.encodeCall(IPluginManager.uninstallPlugin, (address(multiOwnerPlugin), "", "")) }); calls[1] = Call({ target: address(account2), @@ -962,12 +619,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { vm.expectEmit(true, true, true, true); emit PluginUninstalled(address(multiOwnerPlugin), true); - account2.uninstallPlugin({ - plugin: address(multiOwnerPlugin), - config: "", - pluginUninstallData: "", - hookUnapplyData: new bytes[](0) - }); + account2.uninstallPlugin({plugin: address(multiOwnerPlugin), config: "", pluginUninstallData: ""}); ComprehensivePlugin plugin = new ComprehensivePlugin(); bytes32 manifestHash = keccak256(abi.encode(plugin.pluginManifest())); @@ -982,8 +634,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { plugin: address(plugin), manifestHash: manifestHash, pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); } @@ -999,43 +650,12 @@ contract UpgradeableModularAccountPluginManagerTest is Test { plugin: address(plugin), manifestHash: manifestHash, pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); vm.stopPrank(); } - function _installWithInjectHooks() - internal - returns (MockPlugin hooksPlugin, MockPlugin newPlugin, bytes32 manifestHash) - { - hooksPlugin = _installPluginWithExecHooks(); - - manifest.permitAnyExternalAddress = true; - newPlugin = new MockPlugin(manifest); - - manifestHash = keccak256(abi.encode(newPlugin.pluginManifest())); - - IPluginManager.InjectedHook[] memory hooks = new IPluginManager.InjectedHook[](1); - hooks[0] = IPluginManager.InjectedHook( - address(hooksPlugin), IPluginExecutor.executeFromPluginExternal.selector, injectedHooksInfo, "" - ); - - vm.prank(owner2); - vm.expectEmit(true, true, true, true); - emit ReceivedCall(abi.encodeCall(IPlugin.onHookApply, (address(newPlugin), injectedHooksInfo, "")), 0); - vm.expectEmit(true, true, true, true); - emit PluginInstalled(address(newPlugin), manifestHash, new FunctionReference[](0), hooks); - IPluginManager(account2).installPlugin({ - plugin: address(newPlugin), - manifestHash: manifestHash, - pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: hooks - }); - } - function _installPluginWithUninstallErrors(bool shouldDrainGas) internal returns (address) { vm.startPrank(owner2); @@ -1045,8 +665,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { plugin: address(plugin), manifestHash: manifestHash, pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); return address(plugin); } @@ -1062,20 +681,14 @@ contract UpgradeableModularAccountPluginManagerTest is Test { plugin: address(hooksPlugin), manifestHash: keccak256(abi.encode(hooksPlugin.pluginManifest())), pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); MockPlugin plugin = new MockPlugin(manifest); - IPluginManager.InjectedHook[] memory hooks = new IPluginManager.InjectedHook[](1); - hooks[0] = IPluginManager.InjectedHook( - address(hooksPlugin), IPluginExecutor.executeFromPluginExternal.selector, injectedHooksInfo, "" - ); IPluginManager(account2).installPlugin({ plugin: address(plugin), manifestHash: keccak256(abi.encode(plugin.pluginManifest())), pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: hooks + dependencies: new FunctionReference[](0) }); return (address(plugin), address(hooksPlugin)); } diff --git a/test/account/ValidationIntersection.t.sol b/test/account/ValidationIntersection.t.sol index 1ea05cd8..69c49957 100644 --- a/test/account/ValidationIntersection.t.sol +++ b/test/account/ValidationIntersection.t.sol @@ -5,7 +5,6 @@ import {Test} from "forge-std/Test.sol"; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; -import {IPluginManager} from "../../src/interfaces/IPluginManager.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../src/interfaces/erc4337/UserOperation.sol"; @@ -61,22 +60,19 @@ contract ValidationIntersectionTest is Test { plugin: address(noHookPlugin), manifestHash: keccak256(abi.encode(noHookPlugin.pluginManifest())), pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); account1.installPlugin({ plugin: address(oneHookPlugin), manifestHash: keccak256(abi.encode(oneHookPlugin.pluginManifest())), pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); account1.installPlugin({ plugin: address(twoHookPlugin), manifestHash: keccak256(abi.encode(twoHookPlugin.pluginManifest())), pluginInitData: "", - dependencies: new FunctionReference[](0), - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: new FunctionReference[](0) }); vm.stopPrank(); } diff --git a/test/account/phases/AccountStatePhases.t.sol b/test/account/phases/AccountStatePhases.t.sol index e189a2e7..790d657c 100644 --- a/test/account/phases/AccountStatePhases.t.sol +++ b/test/account/phases/AccountStatePhases.t.sol @@ -69,19 +69,12 @@ contract AccountStatePhasesTest is Test { uint8 internal constant _RT_VALIDATION_FUNCTION_ID_6 = 6; // Event re-declarations for vm.expectEmit - event PluginInstalled( - address indexed plugin, - bytes32 manifestHash, - FunctionReference[] dependencies, - IPluginManager.InjectedHook[] injectedHooks - ); + event PluginInstalled(address indexed plugin, bytes32 manifestHash, FunctionReference[] dependencies); event PluginUninstalled(address indexed plugin, bool indexed callbacksSucceeded); event ReceivedCall(bytes msgData, uint256 msgValue); // Empty arrays for convenience FunctionReference[] internal _EMPTY_DEPENDENCIES; - IPluginManager.InjectedHook[] internal _EMPTY_INJECTED_HOOKS; - bytes[] internal _EMPTY_HOOK_APPLY_DATA; // Constants for running user ops uint256 constant CALL_GAS_LIMIT = 300000; @@ -242,9 +235,9 @@ contract AccountStatePhasesTest is Test { function _installASMPlugin() internal { bytes32 manifestHash = _manifestHashOf(asmPlugin.pluginManifest()); vm.expectEmit(true, true, true, true); - emit PluginInstalled(address(asmPlugin), manifestHash, _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS); + emit PluginInstalled(address(asmPlugin), manifestHash, _EMPTY_DEPENDENCIES); vm.prank(owner1); - account1.installPlugin(address(asmPlugin), manifestHash, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS); + account1.installPlugin(address(asmPlugin), manifestHash, "", _EMPTY_DEPENDENCIES); } // Sets up the manifest hash variable and deploys the mock plugin. @@ -259,9 +252,9 @@ contract AccountStatePhasesTest is Test { vm.expectEmit(true, true, true, true); emit ReceivedCall(abi.encodeCall(IPlugin.onInstall, (bytes(""))), 0); vm.expectEmit(true, true, true, true); - emit PluginInstalled(address(mockPlugin1), manifestHash1, _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS); + emit PluginInstalled(address(mockPlugin1), manifestHash1, _EMPTY_DEPENDENCIES); vm.prank(owner1); - account1.installPlugin(address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS); + account1.installPlugin(address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES); } function _manifestHashOf(PluginManifest memory manifest) internal pure returns (bytes32) { @@ -295,14 +288,13 @@ contract AccountStatePhasesTest is Test { calls[0] = Call({ target: address(account1), value: 0 ether, - data: abi.encodeCall(IPluginManager.uninstallPlugin, (address(asmPlugin), "", "", _EMPTY_HOOK_APPLY_DATA)) + data: abi.encodeCall(IPluginManager.uninstallPlugin, (address(asmPlugin), "", "")) }); calls[1] = Call({ target: address(account1), value: 0 ether, data: abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ) }); return calls; diff --git a/test/account/phases/AccountStatePhasesExec.t.sol b/test/account/phases/AccountStatePhasesExec.t.sol index 64c84f72..4107cba5 100644 --- a/test/account/phases/AccountStatePhasesExec.t.sol +++ b/test/account/phases/AccountStatePhasesExec.t.sol @@ -134,8 +134,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.PRE_EXECUTION_HOOK ); @@ -178,7 +177,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { setPreRTValidation: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.PRE_EXECUTION_HOOK ); _installASMPlugin(); @@ -258,7 +257,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { setPreRTValidation: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(asmPlugin), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(asmPlugin), "", "")), AccountStateMutatingPlugin.FunctionId.PRE_EXECUTION_HOOK ); _installASMPlugin(); @@ -300,8 +299,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.PRE_EXECUTION_HOOK ); @@ -348,7 +346,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { setPreRTValidation: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.PRE_EXECUTION_HOOK ); _installASMPlugin(); @@ -387,8 +385,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.PRE_EXECUTION_HOOK ); @@ -441,9 +438,9 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { vm.expectEmit(true, true, true, true); emit ReceivedCall(abi.encodeCall(IPlugin.onInstall, (bytes(""))), 0); vm.expectEmit(true, true, true, true); - emit PluginInstalled(address(mockPlugin2), manifestHash2, _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS); + emit PluginInstalled(address(mockPlugin2), manifestHash2, _EMPTY_DEPENDENCIES); vm.prank(owner1); - account1.installPlugin(address(mockPlugin2), manifestHash2, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS); + account1.installPlugin(address(mockPlugin2), manifestHash2, "", _EMPTY_DEPENDENCIES); // Install the ASM plugin with a pre exec hook that will add a post exec hook. // It also needs a post-only exec hook to ensure that the mock plugin's hook is not the first one. @@ -457,8 +454,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.PRE_EXECUTION_HOOK ); @@ -506,7 +502,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { setPreRTValidation: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.PRE_EXECUTION_HOOK ); _installASMPlugin(); @@ -555,9 +551,9 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { vm.expectEmit(true, true, true, true); emit ReceivedCall(abi.encodeCall(IPlugin.onInstall, (bytes(""))), 0); vm.expectEmit(true, true, true, true); - emit PluginInstalled(address(mockPlugin2), manifestHash2, _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS); + emit PluginInstalled(address(mockPlugin2), manifestHash2, _EMPTY_DEPENDENCIES); vm.prank(owner1); - account1.installPlugin(address(mockPlugin2), manifestHash2, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS); + account1.installPlugin(address(mockPlugin2), manifestHash2, "", _EMPTY_DEPENDENCIES); // Set up the mock plugin with a post-Exec hook, which will be removed and should still run. _initMockPluginPostOnlyExecutionHook(); @@ -575,7 +571,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { setPreRTValidation: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.PRE_EXECUTION_HOOK ); _installASMPlugin(); @@ -644,8 +640,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.EXECUTION_FUNCTION ); @@ -686,8 +681,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.EXECUTION_FUNCTION ); @@ -735,7 +729,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { setPreRTValidation: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.EXECUTION_FUNCTION ); _installASMPlugin(); @@ -777,7 +771,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { setPreRTValidation: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.EXECUTION_FUNCTION ); _installASMPlugin(); @@ -822,8 +816,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.EXECUTION_FUNCTION ); @@ -864,8 +857,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.EXECUTION_FUNCTION ); @@ -913,7 +905,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { setPreRTValidation: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.EXECUTION_FUNCTION ); _installASMPlugin(); @@ -955,7 +947,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { setPreRTValidation: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.EXECUTION_FUNCTION ); _installASMPlugin(); @@ -1031,8 +1023,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.POST_EXECUTION_HOOK ); @@ -1087,7 +1078,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { setPreRTValidation: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.POST_EXECUTION_HOOK ); _installASMPlugin(); @@ -1134,8 +1125,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.POST_EXECUTION_HOOK ); @@ -1182,8 +1172,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.POST_EXECUTION_HOOK ); @@ -1233,7 +1222,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { setPreRTValidation: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.POST_EXECUTION_HOOK ); _installASMPlugin(); @@ -1281,7 +1270,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { setPreRTValidation: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.POST_EXECUTION_HOOK ); _installASMPlugin(); diff --git a/test/account/phases/AccountStatePhasesRTValidation.t.sol b/test/account/phases/AccountStatePhasesRTValidation.t.sol index 5c4dbc1c..809e2ed6 100644 --- a/test/account/phases/AccountStatePhasesRTValidation.t.sol +++ b/test/account/phases/AccountStatePhasesRTValidation.t.sol @@ -100,8 +100,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK ); @@ -146,7 +145,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { setPostExec: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK ); _installASMPlugin(); @@ -273,8 +272,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK ); @@ -315,8 +313,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK ); @@ -365,7 +362,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { setPostExec: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK ); _installASMPlugin(); @@ -446,7 +443,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { setPostExec: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(asmPlugin), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(asmPlugin), "", "")), AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK ); _installASMPlugin(); @@ -490,8 +487,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK ); @@ -532,8 +528,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK ); @@ -576,7 +571,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { setPostExec: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK ); _installASMPlugin(); @@ -618,7 +613,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { setPostExec: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK ); _installASMPlugin(); @@ -657,8 +652,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK ); @@ -699,8 +693,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK ); @@ -749,7 +742,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { setPostExec: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK ); _installASMPlugin(); @@ -791,7 +784,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { setPreExec: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.PRE_RUNTIME_VALIDATION_HOOK ); _installASMPlugin(); @@ -851,8 +844,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.RUNTIME_VALIDATION ); @@ -892,8 +884,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.RUNTIME_VALIDATION ); @@ -941,7 +932,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { setPostExec: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.RUNTIME_VALIDATION ); _installASMPlugin(); @@ -1022,7 +1013,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { setPostExec: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(asmPlugin), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(asmPlugin), "", "")), AccountStateMutatingPlugin.FunctionId.RUNTIME_VALIDATION ); _installASMPlugin(); @@ -1065,8 +1056,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.RUNTIME_VALIDATION ); @@ -1106,8 +1096,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.RUNTIME_VALIDATION ); @@ -1149,7 +1138,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { setPostExec: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.RUNTIME_VALIDATION ); _installASMPlugin(); @@ -1190,7 +1179,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { setPostExec: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.RUNTIME_VALIDATION ); _installASMPlugin(); @@ -1229,8 +1218,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.RUNTIME_VALIDATION ); @@ -1270,8 +1258,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.RUNTIME_VALIDATION ); @@ -1319,7 +1306,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { setPreExec: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.RUNTIME_VALIDATION ); _installASMPlugin(); @@ -1360,7 +1347,7 @@ contract AccountStatePhasesRTValidationTest is AccountStatePhasesTest { setPreExec: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.RUNTIME_VALIDATION ); _installASMPlugin(); diff --git a/test/account/phases/AccountStatePhasesUOValidation.t.sol b/test/account/phases/AccountStatePhasesUOValidation.t.sol index bb8a31dd..e0e79d7d 100644 --- a/test/account/phases/AccountStatePhasesUOValidation.t.sol +++ b/test/account/phases/AccountStatePhasesUOValidation.t.sol @@ -90,8 +90,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK ); @@ -133,7 +132,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { setPostExec: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK ); _installASMPlugin(); @@ -210,7 +209,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(asmPlugin), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(asmPlugin), "", "")), AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK ); _installASMPlugin(); @@ -255,8 +254,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK ); @@ -296,8 +294,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK ); @@ -345,7 +342,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { setPostExec: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK ); _installASMPlugin(); @@ -423,7 +420,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { setPostExec: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(asmPlugin), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(asmPlugin), "", "")), AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK ); _installASMPlugin(); @@ -463,8 +460,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK ); @@ -504,8 +500,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK ); @@ -547,7 +542,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { setPostExec: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK ); _installASMPlugin(); @@ -589,7 +584,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { setPostExec: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK ); _installASMPlugin(); @@ -627,8 +622,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK ); @@ -668,8 +662,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK ); @@ -717,7 +710,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { setPreExec: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK ); _installASMPlugin(); @@ -759,7 +752,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { setPostExec: true }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.PRE_USER_OP_VALIDATION_HOOK ); _installASMPlugin(); @@ -818,8 +811,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.USER_OP_VALIDATION ); @@ -858,8 +850,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.USER_OP_VALIDATION ); @@ -906,7 +897,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { setPostExec: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.USER_OP_VALIDATION ); _installASMPlugin(); @@ -983,7 +974,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { setPostExec: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(asmPlugin), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(asmPlugin), "", "")), AccountStateMutatingPlugin.FunctionId.USER_OP_VALIDATION ); _installASMPlugin(); @@ -1020,8 +1011,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.USER_OP_VALIDATION ); @@ -1061,8 +1051,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.USER_OP_VALIDATION ); @@ -1103,7 +1092,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { setPostExec: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.USER_OP_VALIDATION ); _installASMPlugin(); @@ -1145,7 +1134,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { setPostExec: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.USER_OP_VALIDATION ); _installASMPlugin(); @@ -1183,8 +1172,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.USER_OP_VALIDATION ); @@ -1225,8 +1213,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { }); asmPlugin.setCallback( abi.encodeCall( - IPluginManager.installPlugin, - (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES, _EMPTY_INJECTED_HOOKS) + IPluginManager.installPlugin, (address(mockPlugin1), manifestHash1, "", _EMPTY_DEPENDENCIES) ), AccountStateMutatingPlugin.FunctionId.USER_OP_VALIDATION ); @@ -1273,7 +1260,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { setPreExec: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.USER_OP_VALIDATION ); _installASMPlugin(); @@ -1315,7 +1302,7 @@ contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { setPreExec: false }); asmPlugin.setCallback( - abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "", _EMPTY_HOOK_APPLY_DATA)), + abi.encodeCall(IPluginManager.uninstallPlugin, (address(mockPlugin1), "", "")), AccountStateMutatingPlugin.FunctionId.USER_OP_VALIDATION ); _installASMPlugin(); diff --git a/test/helpers/KnownSelectors.t.sol b/test/helpers/KnownSelectors.t.sol index 4e881ddc..864f9113 100644 --- a/test/helpers/KnownSelectors.t.sol +++ b/test/helpers/KnownSelectors.t.sol @@ -49,7 +49,6 @@ contract KnownSelectorsTest is Test { // IAccountLoupe methods assertTrue(KnownSelectors.isNativeFunction(IAccountLoupe.getExecutionFunctionConfig.selector)); assertTrue(KnownSelectors.isNativeFunction(IAccountLoupe.getExecutionHooks.selector)); - assertTrue(KnownSelectors.isNativeFunction(IAccountLoupe.getPermittedCallHooks.selector)); assertTrue(KnownSelectors.isNativeFunction(IAccountLoupe.getPreValidationHooks.selector)); assertTrue(KnownSelectors.isNativeFunction(IAccountLoupe.getInstalledPlugins.selector)); @@ -75,8 +74,6 @@ contract KnownSelectorsTest is Test { assertTrue(KnownSelectors.isIPluginFunction(IPlugin.runtimeValidationFunction.selector)); assertTrue(KnownSelectors.isIPluginFunction(IPlugin.preExecutionHook.selector)); assertTrue(KnownSelectors.isIPluginFunction(IPlugin.postExecutionHook.selector)); - assertTrue(KnownSelectors.isIPluginFunction(IPlugin.onHookApply.selector)); - assertTrue(KnownSelectors.isIPluginFunction(IPlugin.onHookUnapply.selector)); assertTrue(KnownSelectors.isIPluginFunction(IPlugin.pluginManifest.selector)); assertTrue(KnownSelectors.isIPluginFunction(IPlugin.pluginMetadata.selector)); diff --git a/test/mocks/plugins/BadTransferOwnershipPlugin.sol b/test/mocks/plugins/BadTransferOwnershipPlugin.sol index c79d2309..9403974b 100644 --- a/test/mocks/plugins/BadTransferOwnershipPlugin.sol +++ b/test/mocks/plugins/BadTransferOwnershipPlugin.sol @@ -9,7 +9,6 @@ import { PluginManifest, PluginMetadata } from "../../../src/interfaces/IPlugin.sol"; -import {IPluginManager} from "../../../src/interfaces/IPluginManager.sol"; import {BaseTestPlugin} from "./BaseTestPlugin.sol"; import {IMultiOwnerPlugin} from "../../../src/plugins/owner/IMultiOwnerPlugin.sol"; import {IStandardExecutor} from "../../../src/interfaces/IStandardExecutor.sol"; diff --git a/test/mocks/plugins/ComprehensivePlugin.sol b/test/mocks/plugins/ComprehensivePlugin.sol index 40a363ce..2e92bed0 100644 --- a/test/mocks/plugins/ComprehensivePlugin.sol +++ b/test/mocks/plugins/ComprehensivePlugin.sol @@ -241,34 +241,6 @@ contract ComprehensivePlugin is BaseTestPlugin { }) }); - manifest.permittedCallHooks = new ManifestExecutionHook[](2); - manifest.permittedCallHooks[0] = ManifestExecutionHook({ - executionSelector: this.foo.selector, - preExecHook: ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: uint8(FunctionId.PRE_PERMITTED_CALL_EXECUTION_HOOK), - dependencyIndex: 0 // Unused. - }), - postExecHook: ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: uint8(FunctionId.POST_PERMITTED_CALL_EXECUTION_HOOK), - dependencyIndex: 0 // Unused. - }) - }); - manifest.permittedCallHooks[1] = ManifestExecutionHook({ - executionSelector: this.foo.selector, - preExecHook: ManifestFunction({ - functionType: ManifestAssociatedFunctionType.NONE, - functionId: 0, // Unused. - dependencyIndex: 0 // Unused. - }), - postExecHook: ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: uint8(FunctionId.POST_PERMITTED_CALL_EXECUTION_HOOK), - dependencyIndex: 0 // Unused. - }) - }); - return manifest; } diff --git a/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol b/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol index 276e35d0..482a143d 100644 --- a/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol +++ b/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol @@ -265,133 +265,6 @@ contract EFPCallerPluginAnyExternalCanSpendNativeToken is BaseTestPlugin { } } -// Create pre and post permitted call hooks for calling ResultCreatorPlugin.foo via `executeFromPlugin` -contract EFPPermittedCallHookPlugin is BaseTestPlugin { - bool public preExecHookCalled; - bool public postExecHookCalled; - - function preExecutionHook(uint8, address, uint256, bytes calldata) external override returns (bytes memory) { - preExecHookCalled = true; - return "context for post exec hook"; - } - - function postExecutionHook(uint8, bytes calldata preExecHookData) external override { - require( - keccak256(preExecHookData) == keccak256("context for post exec hook"), "Invalid pre exec hook data" - ); - postExecHookCalled = true; - } - - function onInstall(bytes calldata) external override {} - - function onUninstall(bytes calldata) external override {} - - function pluginManifest() external pure override returns (PluginManifest memory) { - PluginManifest memory manifest; - - manifest.executionFunctions = new bytes4[](1); - manifest.executionFunctions[0] = this.performEFPCall.selector; - - manifest.runtimeValidationFunctions = new ManifestAssociatedFunction[](1); - manifest.runtimeValidationFunctions[0] = ManifestAssociatedFunction({ - executionSelector: this.performEFPCall.selector, - associatedFunction: ManifestFunction({ - functionType: ManifestAssociatedFunctionType.RUNTIME_VALIDATION_ALWAYS_ALLOW, - functionId: 0, - dependencyIndex: 0 - }) - }); - - manifest.permittedCallHooks = new ManifestExecutionHook[](1); - manifest.permittedCallHooks[0] = ManifestExecutionHook({ - executionSelector: ResultCreatorPlugin.foo.selector, - preExecHook: ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: 0, - dependencyIndex: 0 - }), - postExecHook: ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: 0, - dependencyIndex: 0 - }) - }); - - manifest.permittedExecutionSelectors = new bytes4[](1); - manifest.permittedExecutionSelectors[0] = ResultCreatorPlugin.foo.selector; - - return manifest; - } - - function performEFPCall() external returns (bytes memory) { - return IPluginExecutor(msg.sender).executeFromPlugin(abi.encodeCall(ResultCreatorPlugin.foo, ())); - } -} - -// Creates pre and post permitted call hooks for `executeFromPluginExternal` -contract EFPExternalPermittedCallHookPlugin is BaseTestPlugin { - bool public preExecHookCalled; - bool public postExecHookCalled; - - function onInstall(bytes calldata) external override {} - - function onUninstall(bytes calldata) external override {} - - function preExecutionHook(uint8, address, uint256, bytes calldata) external override returns (bytes memory) { - preExecHookCalled = true; - return "context for post exec hook"; - } - - function postExecutionHook(uint8, bytes calldata preExecHookData) external override { - require( - keccak256(preExecHookData) == keccak256("context for post exec hook"), "Invalid pre exec hook data" - ); - postExecHookCalled = true; - } - - function pluginManifest() external pure override returns (PluginManifest memory) { - PluginManifest memory manifest; - - manifest.executionFunctions = new bytes4[](1); - manifest.executionFunctions[0] = this.performIncrement.selector; - - manifest.runtimeValidationFunctions = new ManifestAssociatedFunction[](1); - manifest.runtimeValidationFunctions[0] = ManifestAssociatedFunction({ - executionSelector: this.performIncrement.selector, - associatedFunction: ManifestFunction({ - functionType: ManifestAssociatedFunctionType.RUNTIME_VALIDATION_ALWAYS_ALLOW, - functionId: 0, - dependencyIndex: 0 - }) - }); - - manifest.permittedCallHooks = new ManifestExecutionHook[](1); - manifest.permittedCallHooks[0] = ManifestExecutionHook({ - executionSelector: IPluginExecutor.executeFromPluginExternal.selector, - preExecHook: ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: 0, - dependencyIndex: 0 - }), - postExecHook: ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: 0, - dependencyIndex: 0 - }) - }); - - manifest.permitAnyExternalAddress = true; - - return manifest; - } - - function performIncrement() external { - IPluginExecutor(msg.sender).executeFromPluginExternal( - counter1, 0, abi.encodeWithSelector(Counter.increment.selector) - ); - } -} - // Create pre and post execution hooks for calling ResultCreatorPlugin.foo, and add a function that calls it via // `executeFromPlugin` contract EFPExecutionHookPlugin is BaseTestPlugin { diff --git a/test/mocks/plugins/UninstallErrorsPlugin.sol b/test/mocks/plugins/UninstallErrorsPlugin.sol index 205369ec..60731795 100644 --- a/test/mocks/plugins/UninstallErrorsPlugin.sol +++ b/test/mocks/plugins/UninstallErrorsPlugin.sol @@ -10,7 +10,6 @@ import { PluginManifest, PluginMetadata } from "../../../src/interfaces/IPlugin.sol"; -import {IPluginManager} from "../../../src/interfaces/IPluginManager.sol"; import {BaseTestPlugin} from "./BaseTestPlugin.sol"; /// Mock plugin that reverts in its uninstall callbacks. Can be configured to @@ -32,14 +31,6 @@ contract UninstallErrorsPlugin is BaseTestPlugin { _revert(); } - function onHookUnapply(address, IPluginManager.InjectedHooksInfo calldata, bytes calldata) - external - virtual - override - { - _revert(); - } - function pluginManifest() external pure override returns (PluginManifest memory manifest) {} function pluginMetadata() external pure override returns (PluginMetadata memory) { diff --git a/test/plugin/TokenReceiverPlugin.t.sol b/test/plugin/TokenReceiverPlugin.t.sol index e9665f9f..ee5e8421 100644 --- a/test/plugin/TokenReceiverPlugin.t.sol +++ b/test/plugin/TokenReceiverPlugin.t.sol @@ -15,7 +15,6 @@ import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAcc import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; import {AccountStorageV1} from "../../src/libraries/AccountStorageV1.sol"; import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; -import {IPluginManager} from "../../src/interfaces/IPluginManager.sol"; import {FunctionReference} from "../../src/libraries/FunctionReferenceLib.sol"; import {MockERC777} from "../mocks/tokens/MockERC777.sol"; @@ -81,11 +80,7 @@ contract TokenReceiverPluginTest is Test, IERC1155Receiver, AccountStorageV1 { function _initPlugin() internal { vm.startPrank(owner); acct.installPlugin( - address(plugin), - keccak256(abi.encode(plugin.pluginManifest())), - bytes(""), - new FunctionReference[](0), - new IPluginManager.InjectedHook[](0) + address(plugin), keccak256(abi.encode(plugin.pluginManifest())), bytes(""), new FunctionReference[](0) ); vm.stopPrank(); } diff --git a/test/plugin/owner/MultiOwnerPluginIntegration.t.sol b/test/plugin/owner/MultiOwnerPluginIntegration.t.sol index 2ffb77e7..85ddd6d1 100644 --- a/test/plugin/owner/MultiOwnerPluginIntegration.t.sol +++ b/test/plugin/owner/MultiOwnerPluginIntegration.t.sol @@ -16,7 +16,6 @@ import {IMultiOwnerPlugin} from "../../../src/plugins/owner/IMultiOwnerPlugin.so import {Utils} from "../../Utils.sol"; import {Call} from "../../../src/interfaces/IStandardExecutor.sol"; import {FunctionReference} from "../../../src/libraries/FunctionReferenceLib.sol"; -import {IPluginManager} from "../../../src/interfaces/IPluginManager.sol"; import {Counter} from "../../mocks/Counter.sol"; import {MultiOwnerMSCAFactory} from "../../../src/factory/MultiOwnerMSCAFactory.sol"; diff --git a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol index 2b0dfeee..e80822a3 100644 --- a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol +++ b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol @@ -16,7 +16,6 @@ import {ISessionKeyPermissionsUpdates} from import {SessionKeyPlugin} from "../../../src/plugins/session/SessionKeyPlugin.sol"; import {IEntryPoint} from "../../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../../src/interfaces/erc4337/UserOperation.sol"; -import {IPluginManager} from "../../../src/interfaces/IPluginManager.sol"; import {FunctionReference, FunctionReferenceLib} from "../../../src/libraries/FunctionReferenceLib.sol"; import {Call} from "../../../src/interfaces/IStandardExecutor.sol"; @@ -85,8 +84,7 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { plugin: address(sessionKeyPlugin), manifestHash: manifestHash, pluginInitData: abi.encode(new address[](0), new bytes32[](0), new bytes[][](0)), - dependencies: dependencies, - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: dependencies }); } @@ -162,7 +160,7 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { function testFuzz_sessionKey_addKeysDuringInstall(uint8 seed) public { // First uninstall the plugin vm.prank(owner1); - account1.uninstallPlugin(address(sessionKeyPlugin), "", "", new bytes[](0)); + account1.uninstallPlugin(address(sessionKeyPlugin), "", ""); // Generate a set of initial session keys uint256 addressCount = (seed % 16) + 1; @@ -195,8 +193,7 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { plugin: address(sessionKeyPlugin), manifestHash: manifestHash, pluginInitData: onInstallData, - dependencies: dependencies, - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: dependencies }); // Check using all view methods diff --git a/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol b/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol index 163234ef..e1eb5d8b 100644 --- a/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol @@ -15,7 +15,6 @@ import {ISessionKeyPermissionsUpdates} from "../../../../src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol"; import {IEntryPoint} from "../../../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../../../src/interfaces/erc4337/UserOperation.sol"; -import {IPluginManager} from "../../../../src/interfaces/IPluginManager.sol"; import {FunctionReference, FunctionReferenceLib} from "../../../../src/libraries/FunctionReferenceLib.sol"; import {Call} from "../../../../src/interfaces/IStandardExecutor.sol"; @@ -87,8 +86,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { plugin: address(sessionKeyPlugin), manifestHash: manifestHash, pluginInitData: abi.encode(new address[](0), new bytes32[](0), new bytes[][](0)), - dependencies: dependencies, - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: dependencies }); // Create and add a session key diff --git a/test/plugin/session/permissions/SessionKeyGasLimits.t.sol b/test/plugin/session/permissions/SessionKeyGasLimits.t.sol index d3ceb443..b2f92d0c 100644 --- a/test/plugin/session/permissions/SessionKeyGasLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyGasLimits.t.sol @@ -15,7 +15,6 @@ import {ISessionKeyPermissionsUpdates} from "../../../../src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol"; import {IEntryPoint} from "../../../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../../../src/interfaces/erc4337/UserOperation.sol"; -import {IPluginManager} from "../../../../src/interfaces/IPluginManager.sol"; import {Call} from "../../../../src/interfaces/IStandardExecutor.sol"; import {FunctionReference, FunctionReferenceLib} from "../../../../src/libraries/FunctionReferenceLib.sol"; @@ -78,8 +77,7 @@ contract SessionKeyGasLimitsTest is Test { plugin: address(sessionKeyPlugin), manifestHash: manifestHash, pluginInitData: abi.encode(new address[](0), new bytes32[](0), new bytes[][](0)), - dependencies: dependencies, - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: dependencies }); // Create and add a session key diff --git a/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol b/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol index 19f23cfa..097c4c45 100644 --- a/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol @@ -15,7 +15,6 @@ import {ISessionKeyPermissionsUpdates} from "../../../../src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol"; import {IEntryPoint} from "../../../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../../../src/interfaces/erc4337/UserOperation.sol"; -import {IPluginManager} from "../../../../src/interfaces/IPluginManager.sol"; import {FunctionReference, FunctionReferenceLib} from "../../../../src/libraries/FunctionReferenceLib.sol"; import {Call} from "../../../../src/interfaces/IStandardExecutor.sol"; @@ -83,8 +82,7 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { plugin: address(sessionKeyPlugin), manifestHash: manifestHash, pluginInitData: abi.encode(new address[](0), new bytes32[](0), new bytes[][](0)), - dependencies: dependencies, - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: dependencies }); // Create and add a session key diff --git a/test/plugin/session/permissions/SessionKeyPermissions.t.sol b/test/plugin/session/permissions/SessionKeyPermissions.t.sol index 8cd62be4..7d1fa402 100644 --- a/test/plugin/session/permissions/SessionKeyPermissions.t.sol +++ b/test/plugin/session/permissions/SessionKeyPermissions.t.sol @@ -15,7 +15,6 @@ import {ISessionKeyPermissionsUpdates} from "../../../../src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol"; import {IEntryPoint} from "../../../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../../../src/interfaces/erc4337/UserOperation.sol"; -import {IPluginManager} from "../../../../src/interfaces/IPluginManager.sol"; import {FunctionReference, FunctionReferenceLib} from "../../../../src/libraries/FunctionReferenceLib.sol"; import {Call} from "../../../../src/interfaces/IStandardExecutor.sol"; @@ -93,8 +92,7 @@ contract SessionKeyPermissionsTest is Test { plugin: address(sessionKeyPlugin), manifestHash: manifestHash, pluginInitData: abi.encode(new address[](0), new bytes32[](0), new bytes[][](0)), - dependencies: dependencies, - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: dependencies }); // Create and add a session key @@ -619,7 +617,7 @@ contract SessionKeyPermissionsTest is Test { // Uninstall the session key plugin vm.prank(owner1); - account1.uninstallPlugin(address(sessionKeyPlugin), "", "", new bytes[](0)); + account1.uninstallPlugin(address(sessionKeyPlugin), "", ""); // Reinstall the session key plugin. vm.startPrank(owner1); @@ -627,8 +625,7 @@ contract SessionKeyPermissionsTest is Test { plugin: address(sessionKeyPlugin), manifestHash: keccak256(abi.encode(sessionKeyPlugin.pluginManifest())), pluginInitData: abi.encode(new address[](0), new bytes32[](0), new bytes[][](0)), - dependencies: dependencies, - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: dependencies }); vm.stopPrank(); @@ -645,7 +642,7 @@ contract SessionKeyPermissionsTest is Test { function testFuzz_initialSessionKeysWithPermissions(uint256 seed) public { // Uninstall the plugin vm.prank(owner1); - account1.uninstallPlugin(address(sessionKeyPlugin), "", "", new bytes[](0)); + account1.uninstallPlugin(address(sessionKeyPlugin), "", ""); address[] memory sessionKeys = _generateRandomAddresses(seed); bytes32[] memory tags = new bytes32[](sessionKeys.length); @@ -669,8 +666,7 @@ contract SessionKeyPermissionsTest is Test { plugin: address(sessionKeyPlugin), manifestHash: manifestHash, pluginInitData: abi.encode(sessionKeys, tags, sessionKeyPermissions), - dependencies: dependencies, - injectedHooks: new IPluginManager.InjectedHook[](0) + dependencies: dependencies }); } diff --git a/test/upgrade/MSCAToMSCA.t.sol b/test/upgrade/MSCAToMSCA.t.sol index 6a571d6f..cf365a0e 100644 --- a/test/upgrade/MSCAToMSCA.t.sol +++ b/test/upgrade/MSCAToMSCA.t.sol @@ -89,8 +89,7 @@ contract MSCAToMSCATest is Test { address(msca), 0, abi.encodeCall( - UpgradeableModularAccount.uninstallPlugin, - (address(multiOwnerPlugin), bytes(""), bytes(""), new bytes[](0)) + UpgradeableModularAccount.uninstallPlugin, (address(multiOwnerPlugin), bytes(""), bytes("")) ) ); From 13755d4e5f636cc526c34ef916780083afb0fa95 Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Mon, 22 Jan 2024 13:32:10 -0800 Subject: [PATCH 075/106] fix: [spearbit-48] Collapse if/else in SessionKeyPermissionsLoupe (#90) --- .../SessionKeyPermissionsLoupe.sol | 53 +++++++------------ 1 file changed, 19 insertions(+), 34 deletions(-) diff --git a/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol b/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol index 900fdaaa..1f2d4590 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol @@ -54,24 +54,19 @@ abstract contract SessionKeyPermissionsLoupe is SessionKeyPermissionsBase { function getNativeTokenSpendLimitInfo(address account, address sessionKey) external view - returns (SpendLimitInfo memory) + returns (SpendLimitInfo memory info) { (SessionKeyData storage sessionKeyData,) = _loadSessionKey(account, sessionKey); - bool hasLimit = !sessionKeyData.nativeTokenSpendLimitBypassed; - - if (hasLimit) { - return SpendLimitInfo({ - hasLimit: true, - limit: sessionKeyData.nativeTokenSpendLimit.limitAmount, - limitUsed: sessionKeyData.nativeTokenSpendLimit.limitUsed, - refreshInterval: sessionKeyData.nativeTokenSpendLimitTimeInfo.refreshInterval, - lastUsedTime: sessionKeyData.nativeTokenSpendLimitTimeInfo.lastUsed - }); - } else { - // The fields aren't cleared until the next time they are set, so report zeros. - return SpendLimitInfo({hasLimit: false, limit: 0, limitUsed: 0, refreshInterval: 0, lastUsedTime: 0}); + if (!sessionKeyData.nativeTokenSpendLimitBypassed) { + info.hasLimit = true; + info.limit = sessionKeyData.nativeTokenSpendLimit.limitAmount; + info.limitUsed = sessionKeyData.nativeTokenSpendLimit.limitUsed; + info.refreshInterval = sessionKeyData.nativeTokenSpendLimitTimeInfo.refreshInterval; + info.lastUsedTime = sessionKeyData.nativeTokenSpendLimitTimeInfo.lastUsed; } + // If the limit is bypassed, report false for hasLimit and zeros for the other fields. + // These are the default values for SpendLimitInfo, so we don't need to set them explicitly. } /// @inheritdoc ISessionKeyPlugin @@ -104,30 +99,20 @@ abstract contract SessionKeyPermissionsLoupe is SessionKeyPermissionsBase { external view override - returns (SpendLimitInfo memory, bool) + returns (SpendLimitInfo memory info, bool shouldReset) { (SessionKeyData storage sessionKeyData,) = _loadSessionKey(account, sessionKey); - bool hasLimit = sessionKeyData.hasGasLimit; - bool shouldReset = sessionKeyData.gasLimitResetThisBundle; + shouldReset = sessionKeyData.gasLimitResetThisBundle; - if (hasLimit) { - return ( - SpendLimitInfo({ - hasLimit: true, - limit: sessionKeyData.gasLimit.limitAmount, - limitUsed: sessionKeyData.gasLimit.limitUsed, - refreshInterval: sessionKeyData.gasLimitTimeInfo.refreshInterval, - lastUsedTime: sessionKeyData.gasLimitTimeInfo.lastUsed - }), - shouldReset - ); - } else { - // The fields aren't cleared until the next time they are set, so report zeros. - return ( - SpendLimitInfo({hasLimit: false, limit: 0, limitUsed: 0, refreshInterval: 0, lastUsedTime: 0}), - shouldReset - ); + if (sessionKeyData.hasGasLimit) { + info.hasLimit = true; + info.limit = sessionKeyData.gasLimit.limitAmount; + info.limitUsed = sessionKeyData.gasLimit.limitUsed; + info.refreshInterval = sessionKeyData.gasLimitTimeInfo.refreshInterval; + info.lastUsedTime = sessionKeyData.gasLimitTimeInfo.lastUsed; } + // If the limit is bypassed, report false for hasLimit and zeros for the other fields. + // These are the default values for SpendLimitInfo, so we don't need to set them explicitly. } } From 9a7bb2abf031921daea2c4e054699a511b07ada1 Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Mon, 22 Jan 2024 14:11:36 -0800 Subject: [PATCH 076/106] fix: [spearbit-68] rename vars in account to be consistent (#91) --- src/account/UpgradeableModularAccount.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index a6d8a190..621558c3 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -142,18 +142,18 @@ contract UpgradeableModularAccount is // execute the function, bubbling up any reverts bool execSuccess = _executeRaw(execPlugin, _convertRuntimeCallBufferToExecBuffer(callBuffer)); - bytes memory execReturnData = _collectReturnData(); + bytes memory returnData = _collectReturnData(); if (!execSuccess) { // Bubble up revert reasons from plugins assembly ("memory-safe") { - revert(add(execReturnData, 32), mload(execReturnData)) + revert(add(returnData, 32), mload(returnData)) } } _doCachedPostHooks(postHooksToRun, postHookArgs); - return execReturnData; + return returnData; } /// @inheritdoc IAccount @@ -233,10 +233,10 @@ contract UpgradeableModularAccount is revert UnrecognizedFunction(selector); } - bool success = _executeRaw(execFunctionPlugin, _convertRuntimeCallBufferToExecBuffer(callBuffer)); + bool execSuccess = _executeRaw(execFunctionPlugin, _convertRuntimeCallBufferToExecBuffer(callBuffer)); returnData = _collectReturnData(); - if (!success) { + if (!execSuccess) { assembly ("memory-safe") { revert(add(returnData, 32), mload(returnData)) } From 5df848530eacdcd189d53456e2c05727f04bc0d1 Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Mon, 22 Jan 2024 14:16:12 -0800 Subject: [PATCH 077/106] fix: [spearbit-69] reorder functions and update comments in plugins to be consistent (#92) --- src/plugins/owner/MultiOwnerPlugin.sol | 66 +++---- src/plugins/session/ISessionKeyPlugin.sol | 12 +- src/plugins/session/SessionKeyPlugin.sol | 169 +++++++++--------- .../SessionKeyPermissionsLoupe.sol | 4 + 4 files changed, 123 insertions(+), 128 deletions(-) diff --git a/src/plugins/owner/MultiOwnerPlugin.sol b/src/plugins/owner/MultiOwnerPlugin.sol index dce18aee..028ade98 100644 --- a/src/plugins/owner/MultiOwnerPlugin.sol +++ b/src/plugins/owner/MultiOwnerPlugin.sol @@ -147,36 +147,6 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { return _1271_MAGIC_VALUE_FAILURE; } - // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ - // ┃ Plugin view functions ┃ - // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - - /// @inheritdoc IMultiOwnerPlugin - function encodeMessageData(address account, bytes memory message) - public - view - override - returns (bytes memory) - { - bytes32 messageHash = keccak256(abi.encode(ERC6900_TYPEHASH, keccak256(message))); - return abi.encodePacked("\x19\x01", _domainSeparator(account), messageHash); - } - - /// @inheritdoc IMultiOwnerPlugin - function getMessageHash(address account, bytes memory message) public view override returns (bytes32) { - return keccak256(encodeMessageData(account, message)); - } - - /// @inheritdoc IMultiOwnerPlugin - function isOwnerOf(address account, address ownerToCheck) public view returns (bool) { - return _owners.contains(account, CastLib.toSetValue(ownerToCheck)); - } - - /// @inheritdoc IMultiOwnerPlugin - function ownersOf(address account) public view returns (address[] memory) { - return CastLib.toAddressArray(_owners.getAll(account)); - } - // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ // ┃ Plugin interface functions ┃ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ @@ -363,9 +333,39 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { return interfaceId == type(IMultiOwnerPlugin).interfaceId || super.supportsInterface(interfaceId); } - // ┏━━━━━━━━━━━━━━━┓ - // ┃ Internal ┃ - // ┗━━━━━━━━━━━━━━━┛ + // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + // ┃ Plugin only view functions ┃ + // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + + /// @inheritdoc IMultiOwnerPlugin + function isOwnerOf(address account, address ownerToCheck) public view returns (bool) { + return _owners.contains(account, CastLib.toSetValue(ownerToCheck)); + } + + /// @inheritdoc IMultiOwnerPlugin + function ownersOf(address account) public view returns (address[] memory) { + return CastLib.toAddressArray(_owners.getAll(account)); + } + + /// @inheritdoc IMultiOwnerPlugin + function encodeMessageData(address account, bytes memory message) + public + view + override + returns (bytes memory) + { + bytes32 messageHash = keccak256(abi.encode(ERC6900_TYPEHASH, keccak256(message))); + return abi.encodePacked("\x19\x01", _domainSeparator(account), messageHash); + } + + /// @inheritdoc IMultiOwnerPlugin + function getMessageHash(address account, bytes memory message) public view override returns (bytes32) { + return keccak256(encodeMessageData(account, message)); + } + + // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + // ┃ Internal Functions ┃ + // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━┛ function _domainSeparator(address account) internal view returns (bytes32) { return keccak256(abi.encode(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION, block.chainid, account, _SALT)); diff --git a/src/plugins/session/ISessionKeyPlugin.sol b/src/plugins/session/ISessionKeyPlugin.sol index 85e35287..72f5b0b1 100644 --- a/src/plugins/session/ISessionKeyPlugin.sol +++ b/src/plugins/session/ISessionKeyPlugin.sol @@ -97,9 +97,9 @@ interface ISessionKeyPlugin { /// @param updates The abi-encoded updates to perform. function updateKeyPermissions(address sessionKey, bytes[] calldata updates) external; - // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ - // ┃ Plugin-only function ┃ - // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + // ┃ Plugin only state updating functions ┃ + // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ /// @notice An externally available function, callable by anyone, that resets the "last used" timestamp on a /// session key. This helps a session key get "unstuck" if it was used in a setting where every call it made @@ -109,9 +109,9 @@ interface ISessionKeyPlugin { /// @param sessionKey The session key to reset. function resetSessionKeyGasLimitTimestamp(address account, address sessionKey) external; - // ┏━━━━━━━━━━━━━━━━━━━━━━┓ - // ┃ View functions ┃ - // ┗━━━━━━━━━━━━━━━━━━━━━━┛ + // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + // ┃ Plugin only view functions ┃ + // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ /// @notice Get the session keys of the account. /// This function is not added to accounts during installation. diff --git a/src/plugins/session/SessionKeyPlugin.sol b/src/plugins/session/SessionKeyPlugin.sol index cec2e47d..3478efa6 100644 --- a/src/plugins/session/SessionKeyPlugin.sol +++ b/src/plugins/session/SessionKeyPlugin.sol @@ -50,53 +50,8 @@ contract SessionKeyPlugin is ISessionKeyPlugin, SessionKeyPermissions, BasePlugi uint256 internal constant _MANIFEST_DEPENDENCY_INDEX_OWNER_RUNTIME_VALIDATION = 1; // Storage fields - AssociatedLinkedListSet internal _sessionKeys; - // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ - // ┃ Plugin interface functions ┃ - // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - - /// @inheritdoc BasePlugin - function userOpValidationFunction(uint8 functionId, UserOperation calldata userOp, bytes32 userOpHash) - external - override - returns (uint256) - { - if (functionId == uint8(FunctionId.USER_OP_VALIDATION_SESSION_KEY)) { - (Call[] memory calls, address sessionKey) = abi.decode(userOp.callData[4:], (Call[], address)); - bytes32 hash = userOpHash.toEthSignedMessageHash(); - - (address recoveredSig, ECDSA.RecoverError err) = hash.tryRecover(userOp.signature); - if (err == ECDSA.RecoverError.NoError) { - if ( - _sessionKeys.contains(msg.sender, CastLib.toSetValue(sessionKey)) && sessionKey == recoveredSig - ) { - return _checkUserOpPermissions(userOp, calls, sessionKey); - } - return _SIG_VALIDATION_FAILED; - } - revert InvalidSignature(sessionKey); - } - revert NotImplemented(); - } - - /// @inheritdoc BasePlugin - function onUninstall(bytes calldata) external override { - // Unset the key id for all session keys. - address[] memory sessionKeys = CastLib.toAddressArray(_sessionKeys.getAll(msg.sender)); - uint256 length = sessionKeys.length; - for (uint256 i = 0; i < length; ++i) { - _updateSessionKeyId(msg.sender, sessionKeys[i], SessionKeyId.wrap(bytes32(0))); - - emit SessionKeyRemoved(msg.sender, sessionKeys[i]); - } - - _sessionKeys.clear(msg.sender); - // Note that we do not reset the key id counter `_keyIdCounter` for the account, in order to prevent - // permissions configured from a previous installation from being re-used. - } - // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ // ┃ Execution functions ┃ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ @@ -179,47 +134,54 @@ contract SessionKeyPlugin is ISessionKeyPlugin, SessionKeyPermissions, BasePlugi // The function `updateKeyPermissions` is implemented in `SessionKeyPermissions`. - // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ - // ┃ Plugin-only function ┃ - // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + // ┃ Plugin only state updating functions ┃ + // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ // The function `resetSessionKeyGasLimitTimestamp` is implemented in `SessionKeyPermissions`. - // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ - // ┃ Plugin view functions ┃ - // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + // ┃ Plugin interface functions ┃ + // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - /// @inheritdoc ISessionKeyPlugin - function sessionKeysOf(address account) external view override returns (address[] memory) { - SetValue[] memory values = _sessionKeys.getAll(account); + /// @inheritdoc BasePlugin + function onUninstall(bytes calldata) external override { + // Unset the key id for all session keys. + address[] memory sessionKeys = CastLib.toAddressArray(_sessionKeys.getAll(msg.sender)); + uint256 length = sessionKeys.length; + for (uint256 i = 0; i < length; ++i) { + _updateSessionKeyId(msg.sender, sessionKeys[i], SessionKeyId.wrap(bytes32(0))); - return CastLib.toAddressArray(values); - } + emit SessionKeyRemoved(msg.sender, sessionKeys[i]); + } - /// @inheritdoc ISessionKeyPlugin - function isSessionKeyOf(address account, address sessionKey) external view override returns (bool) { - return _sessionKeys.contains(account, CastLib.toSetValue(sessionKey)); + _sessionKeys.clear(msg.sender); + // Note that we do not reset the key id counter `_keyIdCounter` for the account, in order to prevent + // permissions configured from a previous installation from being re-used. } - // ┏━━━━━━━━━━━━━━━━━━━━━━┓ - // ┃ View functions ┃ - // ┗━━━━━━━━━━━━━━━━━━━━━━┛ - - /// @inheritdoc ISessionKeyPlugin - function findPredecessor(address account, address sessionKey) external view override returns (bytes32) { - address[] memory sessionKeys = CastLib.toAddressArray(_sessionKeys.getAll(account)); + /// @inheritdoc BasePlugin + function userOpValidationFunction(uint8 functionId, UserOperation calldata userOp, bytes32 userOpHash) + external + override + returns (uint256) + { + if (functionId == uint8(FunctionId.USER_OP_VALIDATION_SESSION_KEY)) { + (Call[] memory calls, address sessionKey) = abi.decode(userOp.callData[4:], (Call[], address)); + bytes32 hash = userOpHash.toEthSignedMessageHash(); - uint256 length = sessionKeys.length; - bytes32 predecessor = SENTINEL_VALUE; - for (uint256 i = 0; i < length; ++i) { - if (sessionKeys[i] == sessionKey) { - return predecessor; + (address recoveredSig, ECDSA.RecoverError err) = hash.tryRecover(userOp.signature); + if (err == ECDSA.RecoverError.NoError) { + if ( + _sessionKeys.contains(msg.sender, CastLib.toSetValue(sessionKey)) && sessionKey == recoveredSig + ) { + return _checkUserOpPermissions(userOp, calls, sessionKey); + } + return _SIG_VALIDATION_FAILED; } - - predecessor = bytes32(bytes20(sessionKeys[i])); + revert InvalidSignature(sessionKey); } - - revert SessionKeyNotFound(sessionKey); + revert NotImplemented(); } /// @inheritdoc BasePlugin @@ -347,19 +309,6 @@ contract SessionKeyPlugin is ISessionKeyPlugin, SessionKeyPermissions, BasePlugi return metadata; } - // ┏━━━━━━━━━━━━━━━┓ - // ┃ EIP-165 ┃ - // ┗━━━━━━━━━━━━━━━┛ - - /// @inheritdoc BasePlugin - function supportsInterface(bytes4 interfaceId) public view override returns (bool) { - return interfaceId == type(ISessionKeyPlugin).interfaceId || super.supportsInterface(interfaceId); - } - - // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━┓ - // ┃ Internal Functions ┃ - // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - /// @inheritdoc BasePlugin function _onInstall(bytes calldata data) internal override isNotInitialized(msg.sender) { (address[] memory sessionKeysToAdd, bytes32[] memory tags,) = @@ -397,9 +346,51 @@ contract SessionKeyPlugin is ISessionKeyPlugin, SessionKeyPermissions, BasePlugi addSessionKey(sessionKeysToAdd[i], tags[i], permissionUpdates[i]); } } - /// @inheritdoc BasePlugin + function _isInitialized(address account) internal view override returns (bool) { return !_sessionKeys.isEmpty(account); } + + // ┏━━━━━━━━━━━━━━━┓ + // ┃ EIP-165 ┃ + // ┗━━━━━━━━━━━━━━━┛ + + /// @inheritdoc BasePlugin + function supportsInterface(bytes4 interfaceId) public view override returns (bool) { + return interfaceId == type(ISessionKeyPlugin).interfaceId || super.supportsInterface(interfaceId); + } + + // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + // ┃ Plugin only view functions ┃ + // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + + /// @inheritdoc ISessionKeyPlugin + function sessionKeysOf(address account) external view override returns (address[] memory) { + SetValue[] memory values = _sessionKeys.getAll(account); + + return CastLib.toAddressArray(values); + } + + /// @inheritdoc ISessionKeyPlugin + function isSessionKeyOf(address account, address sessionKey) external view override returns (bool) { + return _sessionKeys.contains(account, CastLib.toSetValue(sessionKey)); + } + + /// @inheritdoc ISessionKeyPlugin + function findPredecessor(address account, address sessionKey) external view override returns (bytes32) { + address[] memory sessionKeys = CastLib.toAddressArray(_sessionKeys.getAll(account)); + + uint256 length = sessionKeys.length; + bytes32 predecessor = SENTINEL_VALUE; + for (uint256 i = 0; i < length; ++i) { + if (sessionKeys[i] == sessionKey) { + return predecessor; + } + + predecessor = bytes32(bytes20(sessionKeys[i])); + } + + revert SessionKeyNotFound(sessionKey); + } } diff --git a/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol b/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol index 1f2d4590..46e84e58 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol @@ -5,6 +5,10 @@ import {ISessionKeyPlugin} from "../ISessionKeyPlugin.sol"; import {SessionKeyPermissionsBase} from "./SessionKeyPermissionsBase.sol"; abstract contract SessionKeyPermissionsLoupe is SessionKeyPermissionsBase { + // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + // ┃ Plugin only view functions ┃ + // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + /// @inheritdoc ISessionKeyPlugin function getAccessControlType(address account, address sessionKey) external From e90b4251db8b30f802c494d16ed340ae240d4d0a Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Mon, 22 Jan 2024 18:03:56 -0500 Subject: [PATCH 078/106] fix: [spearbit-60] add comment to explain that view functions are not given user op validation functions (#95) --- src/plugins/owner/MultiOwnerPlugin.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/owner/MultiOwnerPlugin.sol b/src/plugins/owner/MultiOwnerPlugin.sol index 028ade98..ebe23226 100644 --- a/src/plugins/owner/MultiOwnerPlugin.sol +++ b/src/plugins/owner/MultiOwnerPlugin.sol @@ -229,6 +229,8 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { }); // Update Modular Account's native functions to use userOpValidationFunction provided by this plugin + // The view functions `isValidSignature` and `eip712Domain` are excluded from being assigned a user + // operation validation function since they should only be called via the runtime path. manifest.userOpValidationFunctions = new ManifestAssociatedFunction[](6); manifest.userOpValidationFunctions[0] = ManifestAssociatedFunction({ executionSelector: this.updateOwners.selector, From 0d607ed185fb6f4c7eb733411bce42ab6f7956ac Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Mon, 22 Jan 2024 15:16:03 -0800 Subject: [PATCH 079/106] fix: [spearbit-77] Refactor session key loading util functions (#93) --- .../permissions/SessionKeyPermissions.sol | 4 ++-- .../permissions/SessionKeyPermissionsBase.sol | 24 ++++++++++++------- .../SessionKeyPermissionsLoupe.sol | 20 +++++++--------- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/plugins/session/permissions/SessionKeyPermissions.sol b/src/plugins/session/permissions/SessionKeyPermissions.sol index 87e354e4..21c1e172 100644 --- a/src/plugins/session/permissions/SessionKeyPermissions.sol +++ b/src/plugins/session/permissions/SessionKeyPermissions.sol @@ -22,7 +22,7 @@ abstract contract SessionKeyPermissions is ISessionKeyPlugin, SessionKeyPermissi /// @inheritdoc ISessionKeyPlugin function updateKeyPermissions(address sessionKey, bytes[] calldata updates) public override { - (SessionKeyData storage sessionKeyData, SessionKeyId keyId) = _loadSessionKey(msg.sender, sessionKey); + (SessionKeyData storage sessionKeyData, SessionKeyId keyId) = _loadSessionKeyData(msg.sender, sessionKey); uint256 length = updates.length; for (uint256 i = 0; i < length; ++i) { @@ -34,7 +34,7 @@ abstract contract SessionKeyPermissions is ISessionKeyPlugin, SessionKeyPermissi /// @inheritdoc ISessionKeyPlugin function resetSessionKeyGasLimitTimestamp(address account, address sessionKey) external override { - (SessionKeyData storage sessionKeyData,) = _loadSessionKey(account, sessionKey); + (SessionKeyData storage sessionKeyData,) = _loadSessionKeyData(account, sessionKey); if (sessionKeyData.gasLimitResetThisBundle) { sessionKeyData.gasLimitResetThisBundle = false; sessionKeyData.gasLimitTimeInfo.lastUsed = uint48(block.timestamp); diff --git a/src/plugins/session/permissions/SessionKeyPermissionsBase.sol b/src/plugins/session/permissions/SessionKeyPermissionsBase.sol index 8decdb07..1632584d 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsBase.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsBase.sol @@ -103,12 +103,6 @@ abstract contract SessionKeyPermissionsBase is ISessionKeyPlugin { // Internal Functions - function _assertKeyExists(SessionKeyId id, address sessionKey) internal pure { - if (SessionKeyId.unwrap(id) == bytes32(0)) { - revert InvalidSessionKey(sessionKey); - } - } - function _sessionKeyIdOf(address associated, address sessionKey) internal view returns (SessionKeyId keyId) { uint256 prefixAndBatchIndex = uint256(bytes32(SESSION_KEY_ID_PREFIX)); bytes memory associatedStorageKey = @@ -120,6 +114,19 @@ abstract contract SessionKeyPermissionsBase is ISessionKeyPlugin { } } + /// @dev Helper function that loads the session key id and asserts it is registered. + function _loadSessionKeyId(address associated, address sessionKey) + internal + view + returns (SessionKeyId keyId) + { + SessionKeyId id = _sessionKeyIdOf(associated, sessionKey); + if (SessionKeyId.unwrap(id) == bytes32(0)) { + revert InvalidSessionKey(sessionKey); + } + return id; + } + function _updateSessionKeyId(address associated, address sessionKey, SessionKeyId newId) internal { uint256 prefixAndBatchIndex = uint256(bytes32(SESSION_KEY_ID_PREFIX)); bytes memory associatedStorageKey = @@ -146,13 +153,12 @@ abstract contract SessionKeyPermissionsBase is ISessionKeyPlugin { /// @dev Helper function that loads the session key id, asserts it is registered, and returns the session key /// data and the key id. - function _loadSessionKey(address associated, address sessionKey) + function _loadSessionKeyData(address associated, address sessionKey) internal view returns (SessionKeyData storage sessionKeyData, SessionKeyId keyId) { - SessionKeyId id = _sessionKeyIdOf(associated, sessionKey); - _assertKeyExists(id, sessionKey); + SessionKeyId id = _loadSessionKeyId(associated, sessionKey); return (_sessionKeyDataOf(associated, id), id); } diff --git a/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol b/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol index 46e84e58..850e2106 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol @@ -15,7 +15,7 @@ abstract contract SessionKeyPermissionsLoupe is SessionKeyPermissionsBase { view returns (ContractAccessControlType) { - (SessionKeyData storage sessionKeyData,) = _loadSessionKey(account, sessionKey); + (SessionKeyData storage sessionKeyData,) = _loadSessionKeyData(account, sessionKey); return sessionKeyData.contractAccessControlType; } @@ -25,8 +25,7 @@ abstract contract SessionKeyPermissionsLoupe is SessionKeyPermissionsBase { view returns (bool isOnList, bool checkSelectors) { - SessionKeyId keyId = _sessionKeyIdOf(account, sessionKey); - _assertKeyExists(keyId, sessionKey); + SessionKeyId keyId = _loadSessionKeyId(account, sessionKey); ContractData storage contractData = _contractDataOf(account, keyId, contractAddress); return (contractData.isOnList, contractData.checkSelectors); } @@ -38,8 +37,7 @@ abstract contract SessionKeyPermissionsLoupe is SessionKeyPermissionsBase { address contractAddress, bytes4 selector ) external view returns (bool isOnList) { - SessionKeyId keyId = _sessionKeyIdOf(account, sessionKey); - _assertKeyExists(keyId, sessionKey); + SessionKeyId keyId = _loadSessionKeyId(account, sessionKey); FunctionData storage functionData = _functionDataOf(account, keyId, contractAddress, selector); return functionData.isOnList; } @@ -50,7 +48,7 @@ abstract contract SessionKeyPermissionsLoupe is SessionKeyPermissionsBase { view returns (uint48 validAfter, uint48 validUntil) { - (SessionKeyData storage sessionKeyData,) = _loadSessionKey(account, sessionKey); + (SessionKeyData storage sessionKeyData,) = _loadSessionKeyData(account, sessionKey); return (sessionKeyData.validAfter, sessionKeyData.validUntil); } @@ -60,7 +58,7 @@ abstract contract SessionKeyPermissionsLoupe is SessionKeyPermissionsBase { view returns (SpendLimitInfo memory info) { - (SessionKeyData storage sessionKeyData,) = _loadSessionKey(account, sessionKey); + (SessionKeyData storage sessionKeyData,) = _loadSessionKeyData(account, sessionKey); if (!sessionKeyData.nativeTokenSpendLimitBypassed) { info.hasLimit = true; @@ -79,7 +77,7 @@ abstract contract SessionKeyPermissionsLoupe is SessionKeyPermissionsBase { view returns (SpendLimitInfo memory) { - (, SessionKeyId keyId) = _loadSessionKey(account, sessionKey); + SessionKeyId keyId = _loadSessionKeyId(account, sessionKey); ContractData storage tokenContractData = _contractDataOf(account, keyId, token); return SpendLimitInfo({ hasLimit: tokenContractData.isERC20WithSpendLimit, @@ -92,9 +90,7 @@ abstract contract SessionKeyPermissionsLoupe is SessionKeyPermissionsBase { /// @inheritdoc ISessionKeyPlugin function getRequiredPaymaster(address account, address sessionKey) external view returns (address) { - SessionKeyId id = _sessionKeyIdOf(account, sessionKey); - _assertKeyExists(id, sessionKey); - SessionKeyData storage sessionKeyData = _sessionKeyDataOf(account, id); + (SessionKeyData storage sessionKeyData,) = _loadSessionKeyData(account, sessionKey); return sessionKeyData.hasRequiredPaymaster ? sessionKeyData.requiredPaymaster : address(0); } @@ -105,7 +101,7 @@ abstract contract SessionKeyPermissionsLoupe is SessionKeyPermissionsBase { override returns (SpendLimitInfo memory info, bool shouldReset) { - (SessionKeyData storage sessionKeyData,) = _loadSessionKey(account, sessionKey); + (SessionKeyData storage sessionKeyData,) = _loadSessionKeyData(account, sessionKey); shouldReset = sessionKeyData.gasLimitResetThisBundle; From 897387ba44e2bc383c4db73cbb87a6f578d8a174 Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Mon, 22 Jan 2024 16:02:31 -0800 Subject: [PATCH 080/106] fix: [spearbit-56] update comments for factory withdraw functions (#96) --- src/factory/MultiOwnerMSCAFactory.sol | 4 ++-- src/factory/MultiOwnerTokenReceiverMSCAFactory.sol | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/factory/MultiOwnerMSCAFactory.sol b/src/factory/MultiOwnerMSCAFactory.sol index 71318905..4fb7a27c 100644 --- a/src/factory/MultiOwnerMSCAFactory.sol +++ b/src/factory/MultiOwnerMSCAFactory.sol @@ -101,8 +101,8 @@ contract MultiOwnerMSCAFactory is Ownable2Step { } /// @notice Withdraw funds from this contract - /// @dev can withdraw stuck erc20s - /// @param to address to send native currency to + /// @dev can withdraw stuck erc20s or native currency + /// @param to address to send erc20s or native currency to /// @param token address of the token to withdraw, 0 address for native currency /// @param amount amount of the token to withdraw in case of rebasing tokens function withdraw(address payable to, address token, uint256 amount) external onlyOwner { diff --git a/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol b/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol index 4940fbbd..14df1ea2 100644 --- a/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol +++ b/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol @@ -109,8 +109,8 @@ contract MultiOwnerTokenReceiverMSCAFactory is Ownable2Step { } /// @notice Withdraw funds from this contract - /// @dev can withdraw stuck erc20s - /// @param to address to send native currency to + /// @dev can withdraw stuck erc20s or native currency + /// @param to address to send erc20s or native currency to /// @param token address of the token to withdraw, 0 address for native currency /// @param amount amount of the token to withdraw in case of rebasing tokens function withdraw(address payable to, address token, uint256 amount) external onlyOwner { From ec3c91d91313e5fd8deb27c2716e7dfabd8bd466 Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Mon, 22 Jan 2024 16:09:52 -0800 Subject: [PATCH 081/106] fix: [spearbit-40] Evaluate keccak expressions at compile time when possible (#98) --- src/libraries/AccountStorageV1.sol | 1 + src/libraries/AssociatedLinkedListSetLib.sol | 1 + src/plugins/owner/MultiOwnerPlugin.sol | 5 ++--- .../session/permissions/SessionKeyPermissionsBase.sol | 8 ++++---- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/libraries/AccountStorageV1.sol b/src/libraries/AccountStorageV1.sol index fd2be094..ac2a18ca 100644 --- a/src/libraries/AccountStorageV1.sol +++ b/src/libraries/AccountStorageV1.sol @@ -86,6 +86,7 @@ contract AccountStorageV1 { /// bytes = keccak256( /// abi.encode(uint256(keccak256("Alchemy.UpgradeableModularAccount.Storage_V1")) - 1) /// ) & ~bytes32(uint256(0xff)); + /// This cannot be evaluated at compile time because of its use in inline assembly. bytes32 internal constant _V1_STORAGE_SLOT = 0xade46bbfcf6f898a43d541e42556d456ca0bf9b326df8debc0f29d3f811a0300; function _getAccountStorage() internal pure returns (AccountStorage storage storage_) { diff --git a/src/libraries/AssociatedLinkedListSetLib.sol b/src/libraries/AssociatedLinkedListSetLib.sol index 9d5255df..b0532b2b 100644 --- a/src/libraries/AssociatedLinkedListSetLib.sol +++ b/src/libraries/AssociatedLinkedListSetLib.sol @@ -25,6 +25,7 @@ library AssociatedLinkedListSetLib { // Mapping keys exclude the upper 15 bits of the meta bytes, which allows keys to be either a value or the // sentinel. + // This cannot be evaluated at compile time because of its use in inline assembly. bytes4 internal constant _ASSOCIATED_STORAGE_PREFIX = 0xf938c976; // bytes4(keccak256("AssociatedLinkedListSet")) // A custom type representing the index of a storage slot diff --git a/src/plugins/owner/MultiOwnerPlugin.sol b/src/plugins/owner/MultiOwnerPlugin.sol index ebe23226..6c62ea41 100644 --- a/src/plugins/owner/MultiOwnerPlugin.sol +++ b/src/plugins/owner/MultiOwnerPlugin.sol @@ -68,8 +68,7 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { bytes4 internal constant _1271_MAGIC_VALUE = 0x1626ba7e; bytes4 internal constant _1271_MAGIC_VALUE_FAILURE = 0xffffffff; - // keccak256("ERC6900Message(bytes message)"); - bytes32 private constant ERC6900_TYPEHASH = 0xa856bbdae1f2c6e4aa17a75ad7cc5650f184ec4b549174dd7258c9701d663fc6; + bytes32 private constant MODULAR_ACCOUNT_TYPEHASH = keccak256("AlchemyModularAccountMessage(bytes message)"); AssociatedLinkedListSet internal _owners; @@ -356,7 +355,7 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { override returns (bytes memory) { - bytes32 messageHash = keccak256(abi.encode(ERC6900_TYPEHASH, keccak256(message))); + bytes32 messageHash = keccak256(abi.encode(MODULAR_ACCOUNT_TYPEHASH, keccak256(message))); return abi.encodePacked("\x19\x01", _domainSeparator(account), messageHash); } diff --git a/src/plugins/session/permissions/SessionKeyPermissionsBase.sol b/src/plugins/session/permissions/SessionKeyPermissionsBase.sol index 1632584d..6f37c0dc 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsBase.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsBase.sol @@ -69,10 +69,10 @@ abstract contract SessionKeyPermissionsBase is ISessionKeyPlugin { } // Prefixes: - bytes4 internal constant SESSION_KEY_ID_PREFIX = bytes4(0x1a01dae4); // bytes4(keccak256("SessionKeyId")) - bytes4 internal constant SESSION_KEY_DATA_PREFIX = bytes4(0x16bff296); // bytes4(keccak256("SessionKeyData")) - bytes4 internal constant CONTRACT_DATA_PREFIX = bytes4(0x634c29f5); // bytes4(keccak256("ContractData")) - bytes4 internal constant FUNCTION_DATA_PREFIX = bytes4(0xd50536f0); // bytes4(keccak256("FunctionData")) + bytes4 internal constant SESSION_KEY_ID_PREFIX = bytes4(keccak256("SessionKeyId")); + bytes4 internal constant SESSION_KEY_DATA_PREFIX = bytes4(keccak256("SessionKeyData")); + bytes4 internal constant CONTRACT_DATA_PREFIX = bytes4(keccak256("ContractData")); + bytes4 internal constant FUNCTION_DATA_PREFIX = bytes4(keccak256("FunctionData")); // KEY DERIVATION // All of these following keys begin with the associated address, From 8f298004cf7161da2952ac60b9ce5ad9d5a00dd7 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Mon, 22 Jan 2024 19:19:04 -0500 Subject: [PATCH 082/106] fix: [spearbit-65] make custom errors more informative (#94) --- src/plugins/BasePlugin.sol | 24 +++++++++---------- src/plugins/owner/MultiOwnerPlugin.sol | 6 ++--- src/plugins/session/ISessionKeyPlugin.sol | 12 +++++----- src/plugins/session/SessionKeyPlugin.sol | 2 +- .../permissions/SessionKeyPermissions.sol | 10 ++++---- .../plugins/AccountStateMutatingPlugin.sol | 14 +++++------ test/mocks/plugins/BaseTestPlugin.sol | 2 +- test/mocks/plugins/ComprehensivePlugin.sol | 12 +++++----- test/mocks/plugins/ValidationPluginMocks.sol | 4 ++-- .../SessionKeyPluginWithMultiOwner.t.sol | 6 ++++- .../SessionKeyERC20SpendLimits.t.sol | 5 ++-- .../permissions/SessionKeyPermissions.t.sol | 8 +++++-- 12 files changed, 57 insertions(+), 48 deletions(-) diff --git a/src/plugins/BasePlugin.sol b/src/plugins/BasePlugin.sol index 6f05e6f9..d02900cb 100644 --- a/src/plugins/BasePlugin.sol +++ b/src/plugins/BasePlugin.sol @@ -17,8 +17,8 @@ import {UserOperation} from "../interfaces/erc4337/UserOperation.sol"; abstract contract BasePlugin is ERC165, IPlugin { error AlreadyInitialized(); error InvalidAction(); - error NotImplemented(); - error NotContractCaller(); + error NotContractCaller(address caller); + error NotImplemented(bytes4 selector, uint8 functionId); error NotInitialized(); modifier isNotInitialized(address account) { @@ -51,7 +51,7 @@ abstract contract BasePlugin is ERC165, IPlugin { /// modular account. function onInstall(bytes calldata data) external virtual { if (msg.sender.code.length == 0) { - revert NotContractCaller(); + revert NotContractCaller(msg.sender); } _onInstall(data); } @@ -62,7 +62,7 @@ abstract contract BasePlugin is ERC165, IPlugin { /// account. function onUninstall(bytes calldata data) external virtual { (data); - revert NotImplemented(); + revert NotImplemented(msg.sig, 0); } /// @notice Run the pre user operation validation hook specified by the `functionId`. @@ -78,7 +78,7 @@ abstract contract BasePlugin is ERC165, IPlugin { returns (uint256) { (functionId, userOp, userOpHash); - revert NotImplemented(); + revert NotImplemented(msg.sig, functionId); } /// @notice Run the user operation validationFunction specified by the `functionId`. @@ -93,7 +93,7 @@ abstract contract BasePlugin is ERC165, IPlugin { returns (uint256) { (functionId, userOp, userOpHash); - revert NotImplemented(); + revert NotImplemented(msg.sig, functionId); } /// @notice Run the pre runtime validation hook specified by the `functionId`. @@ -108,7 +108,7 @@ abstract contract BasePlugin is ERC165, IPlugin { virtual { (functionId, sender, value, data); - revert NotImplemented(); + revert NotImplemented(msg.sig, functionId); } /// @notice Run the runtime validationFunction specified by the `functionId`. @@ -123,7 +123,7 @@ abstract contract BasePlugin is ERC165, IPlugin { virtual { (functionId, sender, value, data); - revert NotImplemented(); + revert NotImplemented(msg.sig, functionId); } /// @notice Run the pre execution hook specified by the `functionId`. @@ -140,7 +140,7 @@ abstract contract BasePlugin is ERC165, IPlugin { returns (bytes memory) { (functionId, sender, value, data); - revert NotImplemented(); + revert NotImplemented(msg.sig, functionId); } /// @notice Run the post execution hook specified by the `functionId`. @@ -150,14 +150,14 @@ abstract contract BasePlugin is ERC165, IPlugin { /// @param preExecHookData The context returned by its associated pre execution hook. function postExecutionHook(uint8 functionId, bytes calldata preExecHookData) external virtual { (functionId, preExecHookData); - revert NotImplemented(); + revert NotImplemented(msg.sig, functionId); } /// @notice Describe the contents and intended configuration of the plugin. /// @dev This manifest MUST stay constant over time. /// @return A manifest describing the contents and intended configuration of the plugin. function pluginManifest() external pure virtual returns (PluginManifest memory) { - revert NotImplemented(); + revert NotImplemented(msg.sig, 0); } /// @notice Describe the metadata of the plugin. @@ -187,7 +187,7 @@ abstract contract BasePlugin is ERC165, IPlugin { /// modular account. function _onInstall(bytes calldata data) internal virtual { (data); - revert NotImplemented(); + revert NotImplemented(msg.sig, 0); } /// @notice Check if the account has initialized this plugin yet diff --git a/src/plugins/owner/MultiOwnerPlugin.sol b/src/plugins/owner/MultiOwnerPlugin.sol index 6c62ea41..b332a924 100644 --- a/src/plugins/owner/MultiOwnerPlugin.sol +++ b/src/plugins/owner/MultiOwnerPlugin.sol @@ -193,7 +193,7 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { return _SIG_VALIDATION_FAILED; } - revert NotImplemented(); + revert NotImplemented(msg.sig, functionId); } /// @inheritdoc BasePlugin @@ -207,9 +207,9 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { if (sender != msg.sender && !isOwnerOf(msg.sender, sender)) { revert NotAuthorized(); } - } else { - revert NotImplemented(); + return; } + revert NotImplemented(msg.sig, functionId); } /// @inheritdoc BasePlugin diff --git a/src/plugins/session/ISessionKeyPlugin.sol b/src/plugins/session/ISessionKeyPlugin.sol index 72f5b0b1..4041350a 100644 --- a/src/plugins/session/ISessionKeyPlugin.sol +++ b/src/plugins/session/ISessionKeyPlugin.sol @@ -51,15 +51,15 @@ interface ISessionKeyPlugin { /// `ISessionKeyPermissionsUpdates`, and are not external functions implemented by this contract. event PermissionsUpdated(address indexed account, address indexed sessionKey, bytes[] updates); + error ERC20SpendLimitExceeded(address account, address sessionKey, address token); + error InvalidPermissionsUpdate(bytes4 updateSelector); error InvalidSessionKey(address sessionKey); - error SessionKeyNotFound(address sessionKey); - error UnableToRemove(address sessionKey); error InvalidSignature(address sessionKey); - error ERC20SpendLimitExceeded(address account, address sessionKey, address token); - error InvalidPermissionsUpdate(); - error InvalidToken(); - error NativeTokenSpendLimitExceeded(address account, address sessionKey); + error InvalidToken(address token); error LengthMismatch(); + error NativeTokenSpendLimitExceeded(address account, address sessionKey); + error SessionKeyNotFound(address sessionKey); + error UnableToRemove(address sessionKey); // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ // ┃ Execution functions ┃ diff --git a/src/plugins/session/SessionKeyPlugin.sol b/src/plugins/session/SessionKeyPlugin.sol index 3478efa6..569cadfd 100644 --- a/src/plugins/session/SessionKeyPlugin.sol +++ b/src/plugins/session/SessionKeyPlugin.sol @@ -181,7 +181,7 @@ contract SessionKeyPlugin is ISessionKeyPlugin, SessionKeyPermissions, BasePlugi } revert InvalidSignature(sessionKey); } - revert NotImplemented(); + revert NotImplemented(msg.sig, functionId); } /// @inheritdoc BasePlugin diff --git a/src/plugins/session/permissions/SessionKeyPermissions.sol b/src/plugins/session/permissions/SessionKeyPermissions.sol index 21c1e172..689400c0 100644 --- a/src/plugins/session/permissions/SessionKeyPermissions.sol +++ b/src/plugins/session/permissions/SessionKeyPermissions.sol @@ -447,12 +447,12 @@ abstract contract SessionKeyPermissions is ISessionKeyPlugin, SessionKeyPermissi SessionKeyData storage sessionKeyData, bytes calldata update ) internal { + bytes4 updateSelector = bytes4(update); + if (update.length < 4) { - revert InvalidPermissionsUpdate(); + revert InvalidPermissionsUpdate(updateSelector); } - bytes4 updateSelector = bytes4(update); - // If/else chain to find the right interal update function to perform. if (updateSelector == ISessionKeyPermissionsUpdates.setAccessListType.selector) { ContractAccessControlType contractAccessControlType = @@ -483,7 +483,7 @@ abstract contract SessionKeyPermissions is ISessionKeyPlugin, SessionKeyPermissi address requiredPaymaster = abi.decode(update[4:], (address)); _setRequiredPaymaster(sessionKeyData, requiredPaymaster); } else { - revert InvalidPermissionsUpdate(); + revert InvalidPermissionsUpdate(updateSelector); } } @@ -540,7 +540,7 @@ abstract contract SessionKeyPermissions is ISessionKeyPlugin, SessionKeyPermissi internal { if (token == address(0)) { - revert InvalidToken(); + revert InvalidToken(token); } ContractData storage tokenContractData = _contractDataOf(msg.sender, keyId, token); diff --git a/test/mocks/plugins/AccountStateMutatingPlugin.sol b/test/mocks/plugins/AccountStateMutatingPlugin.sol index ae75ffbd..5c246638 100644 --- a/test/mocks/plugins/AccountStateMutatingPlugin.sol +++ b/test/mocks/plugins/AccountStateMutatingPlugin.sol @@ -74,7 +74,7 @@ contract AccountStateMutatingPlugin is BaseTestPlugin { } else if (where == FunctionId.POST_EXECUTION_HOOK) { postExecCallback = callback; } else { - revert NotImplemented(); + revert NotImplemented(msg.sig, uint8(where)); } } @@ -216,7 +216,7 @@ contract AccountStateMutatingPlugin is BaseTestPlugin { _performCallbackIfNonempty(preUOValidationCallback); return 0; } - revert NotImplemented(); + revert NotImplemented(msg.sig, functionId); } function userOpValidationFunction(uint8 functionId, UserOperation calldata, bytes32) @@ -228,7 +228,7 @@ contract AccountStateMutatingPlugin is BaseTestPlugin { _performCallbackIfNonempty(UOValidationCallback); return 0; } - revert NotImplemented(); + revert NotImplemented(msg.sig, functionId); } function preRuntimeValidationHook(uint8 functionId, address, uint256, bytes calldata) external override { @@ -236,7 +236,7 @@ contract AccountStateMutatingPlugin is BaseTestPlugin { _performCallbackIfNonempty(preRTValidationCallback); return; } - revert NotImplemented(); + revert NotImplemented(msg.sig, functionId); } function runtimeValidationFunction(uint8 functionId, address, uint256, bytes calldata) external override { @@ -244,7 +244,7 @@ contract AccountStateMutatingPlugin is BaseTestPlugin { _performCallbackIfNonempty(RTValidationCallback); return; } - revert NotImplemented(); + revert NotImplemented(msg.sig, functionId); } function preExecutionHook(uint8 functionId, address, uint256, bytes calldata) @@ -256,7 +256,7 @@ contract AccountStateMutatingPlugin is BaseTestPlugin { _performCallbackIfNonempty(preExecCallback); return ""; } - revert NotImplemented(); + revert NotImplemented(msg.sig, functionId); } function executionFunction() external { @@ -268,7 +268,7 @@ contract AccountStateMutatingPlugin is BaseTestPlugin { _performCallbackIfNonempty(postExecCallback); return; } - revert NotImplemented(); + revert NotImplemented(msg.sig, functionId); } function _performCallbackIfNonempty(bytes storage callback) internal { diff --git a/test/mocks/plugins/BaseTestPlugin.sol b/test/mocks/plugins/BaseTestPlugin.sol index 3daf3fdb..bbc388b0 100644 --- a/test/mocks/plugins/BaseTestPlugin.sol +++ b/test/mocks/plugins/BaseTestPlugin.sol @@ -7,6 +7,6 @@ import {PluginMetadata} from "../../../src/interfaces/IPlugin.sol"; contract BaseTestPlugin is BasePlugin { // Don't need to implement this in each context function pluginMetadata() external pure virtual override returns (PluginMetadata memory) { - revert NotImplemented(); + revert NotImplemented(msg.sig, 0); } } diff --git a/test/mocks/plugins/ComprehensivePlugin.sol b/test/mocks/plugins/ComprehensivePlugin.sol index 2e92bed0..471ecf4a 100644 --- a/test/mocks/plugins/ComprehensivePlugin.sol +++ b/test/mocks/plugins/ComprehensivePlugin.sol @@ -56,7 +56,7 @@ contract ComprehensivePlugin is BaseTestPlugin { } else if (functionId == uint8(FunctionId.PRE_USER_OP_VALIDATION_HOOK_2)) { return 0; } - revert NotImplemented(); + revert NotImplemented(msg.sig, functionId); } function userOpValidationFunction(uint8 functionId, UserOperation calldata, bytes32) @@ -68,7 +68,7 @@ contract ComprehensivePlugin is BaseTestPlugin { if (functionId == uint8(FunctionId.USER_OP_VALIDATION)) { return 0; } - revert NotImplemented(); + revert NotImplemented(msg.sig, functionId); } function preRuntimeValidationHook(uint8 functionId, address, uint256, bytes calldata) external pure override { @@ -77,7 +77,7 @@ contract ComprehensivePlugin is BaseTestPlugin { } else if (functionId == uint8(FunctionId.PRE_RUNTIME_VALIDATION_HOOK_2)) { return; } - revert NotImplemented(); + revert NotImplemented(msg.sig, functionId); } function runtimeValidationFunction(uint8 functionId, address, uint256, bytes calldata) @@ -88,7 +88,7 @@ contract ComprehensivePlugin is BaseTestPlugin { if (functionId == uint8(FunctionId.RUNTIME_VALIDATION)) { return; } - revert NotImplemented(); + revert NotImplemented(msg.sig, functionId); } function preExecutionHook(uint8 functionId, address, uint256, bytes calldata) @@ -102,7 +102,7 @@ contract ComprehensivePlugin is BaseTestPlugin { } else if (functionId == uint8(FunctionId.PRE_PERMITTED_CALL_EXECUTION_HOOK)) { return ""; } - revert NotImplemented(); + revert NotImplemented(msg.sig, functionId); } function postExecutionHook(uint8 functionId, bytes calldata) external pure override { @@ -111,7 +111,7 @@ contract ComprehensivePlugin is BaseTestPlugin { } else if (functionId == uint8(FunctionId.POST_PERMITTED_CALL_EXECUTION_HOOK)) { return; } - revert NotImplemented(); + revert NotImplemented(msg.sig, functionId); } function pluginManifest() external pure override returns (PluginManifest memory) { diff --git a/test/mocks/plugins/ValidationPluginMocks.sol b/test/mocks/plugins/ValidationPluginMocks.sol index 9687ec8b..1f899217 100644 --- a/test/mocks/plugins/ValidationPluginMocks.sol +++ b/test/mocks/plugins/ValidationPluginMocks.sol @@ -40,7 +40,7 @@ abstract contract MockBaseUserOpValidationPlugin is BaseTestPlugin { } else if (functionId == uint8(FunctionId.PRE_USER_OP_VALIDATION_HOOK_2)) { return _preUserOpValidationHook2Data; } - revert NotImplemented(); + revert NotImplemented(msg.sig, functionId); } function userOpValidationFunction(uint8 functionId, UserOperation calldata, bytes32) @@ -52,7 +52,7 @@ abstract contract MockBaseUserOpValidationPlugin is BaseTestPlugin { if (functionId == uint8(FunctionId.USER_OP_VALIDATION)) { return _userOpValidationFunctionData; } - revert NotImplemented(); + revert NotImplemented(msg.sig, functionId); } } diff --git a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol index e80822a3..61cf172d 100644 --- a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol +++ b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol @@ -438,7 +438,11 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { vm.assume(functionId != uint8(ISessionKeyPlugin.FunctionId.USER_OP_VALIDATION_SESSION_KEY)); bytes32 userOpHash = entryPoint.getUserOpHash(userOp); - vm.expectRevert(abi.encodeWithSelector(BasePlugin.NotImplemented.selector)); + vm.expectRevert( + abi.encodeWithSelector( + BasePlugin.NotImplemented.selector, BasePlugin.userOpValidationFunction.selector, functionId + ) + ); sessionKeyPlugin.userOpValidationFunction(functionId, userOp, userOpHash); } diff --git a/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol b/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol index e1eb5d8b..e84072d6 100644 --- a/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol @@ -178,8 +178,9 @@ contract SessionKeyERC20SpendLimitsTest is Test { function test_sessionKeyERC20SpendLimits_tokenAddressZeroFails() public { bytes[] memory updates = new bytes[](1); - updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setERC20SpendLimit, (address(0), 1000, 0)); - vm.expectRevert(abi.encodeWithSelector(ISessionKeyPlugin.InvalidToken.selector)); + address token; + updates[0] = abi.encodeCall(ISessionKeyPermissionsUpdates.setERC20SpendLimit, (token, 1000, 0)); + vm.expectRevert(abi.encodeWithSelector(ISessionKeyPlugin.InvalidToken.selector, token)); vm.prank(owner1); SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); } diff --git a/test/plugin/session/permissions/SessionKeyPermissions.t.sol b/test/plugin/session/permissions/SessionKeyPermissions.t.sol index 7d1fa402..628446ad 100644 --- a/test/plugin/session/permissions/SessionKeyPermissions.t.sol +++ b/test/plugin/session/permissions/SessionKeyPermissions.t.sol @@ -545,11 +545,15 @@ contract SessionKeyPermissionsTest is Test { vm.startPrank(owner1); bytes[] memory updates = new bytes[](1); updates[0] = hex"112233"; // < 4 byte update - vm.expectRevert(abi.encodeWithSelector(ISessionKeyPlugin.InvalidPermissionsUpdate.selector)); + vm.expectRevert( + abi.encodeWithSelector(ISessionKeyPlugin.InvalidPermissionsUpdate.selector, bytes4(updates[0])) + ); SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); updates[0] = hex"11223344"; // Invalid selector - vm.expectRevert(abi.encodeWithSelector(ISessionKeyPlugin.InvalidPermissionsUpdate.selector)); + vm.expectRevert( + abi.encodeWithSelector(ISessionKeyPlugin.InvalidPermissionsUpdate.selector, bytes4(updates[0])) + ); SessionKeyPlugin(address(account1)).updateKeyPermissions(sessionKey1, updates); } From 2c99fcd980c6d156c99f08aabb608cac18bf1723 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Mon, 22 Jan 2024 19:32:29 -0500 Subject: [PATCH 083/106] refactor: move FunctionReference type to IPluginManager (#100) --- src/account/AccountLoupe.sol | 2 +- src/account/PluginManagerInternals.sol | 23 +++++++++-------- src/account/UpgradeableModularAccount.sol | 9 ++++--- src/interfaces/IAccountLoupe.sol | 2 +- src/interfaces/IPluginManager.sol | 2 +- src/libraries/AccountStorageV1.sol | 2 +- src/libraries/FunctionReferenceLib.sol | 21 ++++++++-------- test/account/AccountExecHooks.t.sol | 3 ++- test/account/AccountLoupe.t.sol | 4 +-- test/account/AccountPreValidationHooks.t.sol | 3 ++- test/account/AccountReturnData.t.sol | 2 +- .../ExecuteFromPluginPermissions.t.sol | 2 +- test/account/ManifestValidity.t.sol | 2 +- test/account/UpgradeableModularAccount.t.sol | 3 +-- ...gradeableModularAccountPluginManager.t.sol | 4 +-- test/account/ValidationIntersection.t.sol | 2 +- test/account/phases/AccountStatePhases.t.sol | 4 +-- test/libraries/FunctionReferenceLib.t.sol | 25 +++++++++++-------- .../ExecFromPluginPermissionsMocks.sol | 2 +- test/mocks/plugins/ManifestValidityMocks.sol | 2 +- test/mocks/plugins/ReturnDataPluginMocks.sol | 2 +- test/plugin/TokenReceiverPlugin.t.sol | 2 +- .../owner/MultiOwnerPluginIntegration.t.sol | 2 +- .../SessionKeyPluginWithMultiOwner.t.sol | 3 ++- .../SessionKeyERC20SpendLimits.t.sol | 3 ++- .../permissions/SessionKeyGasLimits.t.sol | 3 ++- .../SessionKeyNativeTokenSpendLimits.t.sol | 3 ++- .../permissions/SessionKeyPermissions.t.sol | 3 ++- 28 files changed, 76 insertions(+), 64 deletions(-) diff --git a/src/account/AccountLoupe.sol b/src/account/AccountLoupe.sol index 8a8d8131..ef67bcd3 100644 --- a/src/account/AccountLoupe.sol +++ b/src/account/AccountLoupe.sol @@ -4,11 +4,11 @@ pragma solidity ^0.8.22; import {KnownSelectors} from "../helpers/KnownSelectors.sol"; import {IAccountLoupe} from "../interfaces/IAccountLoupe.sol"; +import {FunctionReference} from "../interfaces/IPluginManager.sol"; import {AccountStorageV1} from "../libraries/AccountStorageV1.sol"; import {CastLib} from "../libraries/CastLib.sol"; import {CountableLinkedListSetLib} from "../libraries/CountableLinkedListSetLib.sol"; -import {FunctionReference} from "../libraries/FunctionReferenceLib.sol"; import {LinkedListSet, LinkedListSetLib} from "../libraries/LinkedListSetLib.sol"; /// @title Account Loupe diff --git a/src/account/PluginManagerInternals.sol b/src/account/PluginManagerInternals.sol index 50584ee1..b27d3e56 100644 --- a/src/account/PluginManagerInternals.sol +++ b/src/account/PluginManagerInternals.sol @@ -14,12 +14,12 @@ import { ManifestFunction, PluginManifest } from "../interfaces/IPlugin.sol"; -import {IPluginManager} from "../interfaces/IPluginManager.sol"; +import {FunctionReference, IPluginManager} from "../interfaces/IPluginManager.sol"; import {AccountStorageV1} from "../libraries/AccountStorageV1.sol"; import {CastLib} from "../libraries/CastLib.sol"; import {CountableLinkedListSetLib} from "../libraries/CountableLinkedListSetLib.sol"; -import {FunctionReference, FunctionReferenceLib} from "../libraries/FunctionReferenceLib.sol"; +import {FunctionReferenceLib} from "../libraries/FunctionReferenceLib.sol"; import {LinkedListSet, LinkedListSetLib} from "../libraries/LinkedListSetLib.sol"; /// @title Plugin Manager Internals @@ -28,6 +28,7 @@ import {LinkedListSet, LinkedListSetLib} from "../libraries/LinkedListSetLib.sol abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { using LinkedListSetLib for LinkedListSet; using CountableLinkedListSetLib for LinkedListSet; + using FunctionReferenceLib for FunctionReference; // Grouping of arguments to `uninstallPlugin` to avoid "stack too deep" // errors when building without via-ir. @@ -102,7 +103,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { SelectorData storage selectorData = _getAccountStorage().selectorData[selector]; - if (selectorData.userOpValidation != FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE) { + if (!selectorData.userOpValidation.isEmpty()) { revert UserOpValidationFunctionAlreadySet(selector, validationFunction); } @@ -114,7 +115,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { SelectorData storage selectorData = _getAccountStorage().selectorData[selector]; - if (selectorData.runtimeValidation != FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE) { + if (!selectorData.runtimeValidation.isEmpty()) { revert RuntimeValidationFunctionAlreadySet(selector, validationFunction); } @@ -128,9 +129,9 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { _addHooks(selectorData.executionHooks, selector, preExecHook, postExecHook); - if (preExecHook != FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE) { + if (!preExecHook.isEmpty()) { selectorData.hasPreExecHooks = true; - } else if (postExecHook != FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE) { + } else if (!postExecHook.isEmpty()) { // Only set this flag if the pre hook is empty and the post hook is non-empty. selectorData.hasPostOnlyExecHooks = true; } @@ -159,13 +160,13 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { FunctionReference preExecHook, FunctionReference postExecHook ) internal { - if (preExecHook != FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE) { + if (!preExecHook.isEmpty()) { // add pre or pre/post pair of exec hooks if (!hooks.preHooks.tryIncrement(CastLib.toSetValue(preExecHook))) { revert DuplicateHookLimitExceeded(selector, preExecHook); } - if (postExecHook != FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE) { + if (!postExecHook.isEmpty()) { // can ignore return val of tryEnableFlags here as tryIncrement above must have succeeded hooks.preHooks.tryEnableFlags(CastLib.toSetValue(preExecHook), _PRE_EXEC_HOOK_HAS_POST_FLAG); if (!hooks.associatedPostHooks[preExecHook].tryIncrement(CastLib.toSetValue(postExecHook))) { @@ -186,7 +187,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { internal returns (bool shouldClearHasPreHooks, bool shouldClearHasPostOnlyHooks) { - if (preExecHook != FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE) { + if (!preExecHook.isEmpty()) { // If decrementing results in removal, this also clears the flag _PRE_EXEC_HOOK_HAS_POST_FLAG. // Can ignore the return value because the manifest was checked to match the hash. hooks.preHooks.tryDecrement(CastLib.toSetValue(preExecHook)); @@ -197,7 +198,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { shouldClearHasPreHooks = true; } - if (postExecHook != FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE) { + if (!postExecHook.isEmpty()) { // Remove the associated post-exec hook, if it is set to the expected value. // Can ignore the return value because the manifest was checked to match the hash. hooks.associatedPostHooks[preExecHook].tryDecrement(CastLib.toSetValue(postExecHook)); @@ -655,7 +656,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { } function _assertNotNullFunction(FunctionReference functionReference) internal pure { - if (functionReference == FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE) { + if (functionReference.isEmpty()) { revert NullFunctionReference(); } } diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index 621558c3..ce9a4a9c 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -17,12 +17,12 @@ import {IAccountView} from "../interfaces/IAccountView.sol"; import {IEntryPoint} from "../interfaces/erc4337/IEntryPoint.sol"; import {IPlugin, PluginManifest} from "../interfaces/IPlugin.sol"; import {IPluginExecutor} from "../interfaces/IPluginExecutor.sol"; -import {IPluginManager} from "../interfaces/IPluginManager.sol"; +import {FunctionReference, IPluginManager} from "../interfaces/IPluginManager.sol"; import {UserOperation} from "../interfaces/erc4337/UserOperation.sol"; import {CastLib} from "../libraries/CastLib.sol"; import {CountableLinkedListSetLib} from "../libraries/CountableLinkedListSetLib.sol"; -import {FunctionReference, FunctionReferenceLib} from "../libraries/FunctionReferenceLib.sol"; +import {FunctionReferenceLib} from "../libraries/FunctionReferenceLib.sol"; import {LinkedListSet, LinkedListSetLib} from "../libraries/LinkedListSetLib.sol"; import {UUPSUpgradeable} from "../../ext/UUPSUpgradeable.sol"; @@ -44,6 +44,7 @@ contract UpgradeableModularAccount is { using CountableLinkedListSetLib for LinkedListSet; using LinkedListSetLib for LinkedListSet; + using FunctionReferenceLib for FunctionReference; /// @dev Struct to hold optional configuration data for uninstalling a plugin. This should be encoded and /// passed to the `config` parameter of `uninstallPlugin`. @@ -419,7 +420,7 @@ contract UpgradeableModularAccount is bytes32 userOpHash, bool doPreValidationHooks ) internal returns (uint256 validationData) { - if (userOpValidationFunction == FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE) { + if (userOpValidationFunction.isEmpty()) { revert UserOpValidationFunctionMissing(selector); } @@ -518,7 +519,7 @@ contract UpgradeableModularAccount is { if (runtimeValidationFunction.isEmptyOrMagicValue()) { if ( - runtimeValidationFunction == FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE + runtimeValidationFunction.isEmpty() && ( ( msg.sig != IPluginManager.installPlugin.selector diff --git a/src/interfaces/IAccountLoupe.sol b/src/interfaces/IAccountLoupe.sol index 7503d970..ed794cc9 100644 --- a/src/interfaces/IAccountLoupe.sol +++ b/src/interfaces/IAccountLoupe.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.22; -import {FunctionReference} from "../libraries/FunctionReferenceLib.sol"; +import {FunctionReference} from "./IPluginManager.sol"; /// @title Account Loupe Interface interface IAccountLoupe { diff --git a/src/interfaces/IPluginManager.sol b/src/interfaces/IPluginManager.sol index 5afbb4f8..2d40634d 100644 --- a/src/interfaces/IPluginManager.sol +++ b/src/interfaces/IPluginManager.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.22; -import {FunctionReference} from "../libraries/FunctionReferenceLib.sol"; +type FunctionReference is bytes21; /// @title Plugin Manager Interface interface IPluginManager { diff --git a/src/libraries/AccountStorageV1.sol b/src/libraries/AccountStorageV1.sol index ac2a18ca..1fef8c9b 100644 --- a/src/libraries/AccountStorageV1.sol +++ b/src/libraries/AccountStorageV1.sol @@ -2,8 +2,8 @@ pragma solidity ^0.8.22; import {IPlugin} from "../interfaces/IPlugin.sol"; +import {FunctionReference} from "../interfaces/IPluginManager.sol"; -import {FunctionReference} from "../libraries/FunctionReferenceLib.sol"; import {LinkedListSet} from "../libraries/LinkedListSetLib.sol"; /// @title Account Storage V1 diff --git a/src/libraries/FunctionReferenceLib.sol b/src/libraries/FunctionReferenceLib.sol index 5ee56b55..5f5e5466 100644 --- a/src/libraries/FunctionReferenceLib.sol +++ b/src/libraries/FunctionReferenceLib.sol @@ -1,10 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.22; -type FunctionReference is bytes21; - -using {eq as ==, notEq as !=} for FunctionReference global; -using FunctionReferenceLib for FunctionReference global; +import {FunctionReference} from "../interfaces/IPluginManager.sol"; /// @title Function Reference Lib /// @author Alchemy @@ -30,12 +27,16 @@ library FunctionReferenceLib { function isEmptyOrMagicValue(FunctionReference fr) internal pure returns (bool) { return FunctionReference.unwrap(fr) <= bytes21(uint168(2)); } -} -function eq(FunctionReference a, FunctionReference b) pure returns (bool) { - return FunctionReference.unwrap(a) == FunctionReference.unwrap(b); -} + function isEmpty(FunctionReference fr) internal pure returns (bool) { + return FunctionReference.unwrap(fr) == bytes21(0); + } -function notEq(FunctionReference a, FunctionReference b) pure returns (bool) { - return FunctionReference.unwrap(a) != FunctionReference.unwrap(b); + function eq(FunctionReference a, FunctionReference b) internal pure returns (bool) { + return FunctionReference.unwrap(a) == FunctionReference.unwrap(b); + } + + function notEq(FunctionReference a, FunctionReference b) internal pure returns (bool) { + return FunctionReference.unwrap(a) != FunctionReference.unwrap(b); + } } diff --git a/test/account/AccountExecHooks.t.sol b/test/account/AccountExecHooks.t.sol index c7c694c1..f9ca99bb 100644 --- a/test/account/AccountExecHooks.t.sol +++ b/test/account/AccountExecHooks.t.sol @@ -9,7 +9,8 @@ import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.so import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; -import {FunctionReference, FunctionReferenceLib} from "../../src/libraries/FunctionReferenceLib.sol"; +import {FunctionReference} from "../../src/interfaces/IPluginManager.sol"; +import {FunctionReferenceLib} from "../../src/libraries/FunctionReferenceLib.sol"; import { IPlugin, ManifestExecutionHook, diff --git a/test/account/AccountLoupe.t.sol b/test/account/AccountLoupe.t.sol index 8064af2e..5c91b26d 100644 --- a/test/account/AccountLoupe.t.sol +++ b/test/account/AccountLoupe.t.sol @@ -17,9 +17,9 @@ import { PluginManifest } from "../../src/interfaces/IPlugin.sol"; import {IAccountLoupe} from "../../src/interfaces/IAccountLoupe.sol"; -import {IPluginManager} from "../../src/interfaces/IPluginManager.sol"; +import {FunctionReference, IPluginManager} from "../../src/interfaces/IPluginManager.sol"; import {IStandardExecutor} from "../../src/interfaces/IStandardExecutor.sol"; -import {FunctionReference, FunctionReferenceLib} from "../../src/libraries/FunctionReferenceLib.sol"; +import {FunctionReferenceLib} from "../../src/libraries/FunctionReferenceLib.sol"; import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; import {ComprehensivePlugin} from "../mocks/plugins/ComprehensivePlugin.sol"; diff --git a/test/account/AccountPreValidationHooks.t.sol b/test/account/AccountPreValidationHooks.t.sol index 8836c00b..a461498c 100644 --- a/test/account/AccountPreValidationHooks.t.sol +++ b/test/account/AccountPreValidationHooks.t.sol @@ -11,7 +11,8 @@ import {IMultiOwnerPlugin} from "../../src/plugins/owner/IMultiOwnerPlugin.sol"; import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../src/interfaces/erc4337/UserOperation.sol"; -import {FunctionReference, FunctionReferenceLib} from "../../src/libraries/FunctionReferenceLib.sol"; +import {FunctionReference} from "../../src/interfaces/IPluginManager.sol"; +import {FunctionReferenceLib} from "../../src/libraries/FunctionReferenceLib.sol"; import { IPlugin, PluginManifest, diff --git a/test/account/AccountReturnData.t.sol b/test/account/AccountReturnData.t.sol index d93834c3..a4d67c06 100644 --- a/test/account/AccountReturnData.t.sol +++ b/test/account/AccountReturnData.t.sol @@ -7,8 +7,8 @@ import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.so import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; +import {FunctionReference} from "../../src/interfaces/IPluginManager.sol"; import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; -import {FunctionReference} from "../../src/libraries/FunctionReferenceLib.sol"; import {Call} from "../../src/interfaces/IStandardExecutor.sol"; import { diff --git a/test/account/ExecuteFromPluginPermissions.t.sol b/test/account/ExecuteFromPluginPermissions.t.sol index 328e9d95..c6a52b0a 100644 --- a/test/account/ExecuteFromPluginPermissions.t.sol +++ b/test/account/ExecuteFromPluginPermissions.t.sol @@ -8,8 +8,8 @@ import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.so import {IPlugin} from "../../src/interfaces/IPlugin.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; +import {FunctionReference} from "../../src/interfaces/IPluginManager.sol"; import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; -import {FunctionReference} from "../../src/libraries/FunctionReferenceLib.sol"; import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; diff --git a/test/account/ManifestValidity.t.sol b/test/account/ManifestValidity.t.sol index f28d5095..a547ab95 100644 --- a/test/account/ManifestValidity.t.sol +++ b/test/account/ManifestValidity.t.sol @@ -8,8 +8,8 @@ import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.so import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {PluginManagerInternals} from "../../src/account/PluginManagerInternals.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; +import {FunctionReference} from "../../src/interfaces/IPluginManager.sol"; import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; -import {FunctionReference} from "../../src/libraries/FunctionReferenceLib.sol"; import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; import { diff --git a/test/account/UpgradeableModularAccount.t.sol b/test/account/UpgradeableModularAccount.t.sol index c985f895..e6debda8 100644 --- a/test/account/UpgradeableModularAccount.t.sol +++ b/test/account/UpgradeableModularAccount.t.sol @@ -18,9 +18,8 @@ import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../src/interfaces/erc4337/UserOperation.sol"; import {IAccountInitializable} from "../../src/interfaces/IAccountInitializable.sol"; import {IPlugin, PluginManifest} from "../../src/interfaces/IPlugin.sol"; -import {IPluginManager} from "../../src/interfaces/IPluginManager.sol"; +import {FunctionReference, IPluginManager} from "../../src/interfaces/IPluginManager.sol"; import {Call} from "../../src/interfaces/IStandardExecutor.sol"; -import {FunctionReference} from "../../src/libraries/FunctionReferenceLib.sol"; import {Counter} from "../mocks/Counter.sol"; import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; diff --git a/test/account/UpgradeableModularAccountPluginManager.t.sol b/test/account/UpgradeableModularAccountPluginManager.t.sol index 49f70a4b..0ea579bd 100644 --- a/test/account/UpgradeableModularAccountPluginManager.t.sol +++ b/test/account/UpgradeableModularAccountPluginManager.t.sol @@ -16,10 +16,10 @@ import {TokenReceiverPlugin} from "../../src/plugins/TokenReceiverPlugin.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; import {PluginManifest} from "../../src/interfaces/IPlugin.sol"; import {IAccountLoupe} from "../../src/interfaces/IAccountLoupe.sol"; -import {IPluginManager} from "../../src/interfaces/IPluginManager.sol"; +import {FunctionReference, IPluginManager} from "../../src/interfaces/IPluginManager.sol"; import {IStandardExecutor} from "../../src/interfaces/IStandardExecutor.sol"; import {Call} from "../../src/interfaces/IStandardExecutor.sol"; -import {FunctionReference, FunctionReferenceLib} from "../../src/libraries/FunctionReferenceLib.sol"; +import {FunctionReferenceLib} from "../../src/libraries/FunctionReferenceLib.sol"; import {IPlugin, PluginManifest} from "../../src/interfaces/IPlugin.sol"; import {Counter} from "../mocks/Counter.sol"; diff --git a/test/account/ValidationIntersection.t.sol b/test/account/ValidationIntersection.t.sol index 69c49957..3e1f192b 100644 --- a/test/account/ValidationIntersection.t.sol +++ b/test/account/ValidationIntersection.t.sol @@ -8,8 +8,8 @@ import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.so import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../src/interfaces/erc4337/UserOperation.sol"; +import {FunctionReference} from "../../src/interfaces/IPluginManager.sol"; import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; -import {FunctionReference} from "../../src/libraries/FunctionReferenceLib.sol"; import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; import { diff --git a/test/account/phases/AccountStatePhases.t.sol b/test/account/phases/AccountStatePhases.t.sol index 790d657c..a0627023 100644 --- a/test/account/phases/AccountStatePhases.t.sol +++ b/test/account/phases/AccountStatePhases.t.sol @@ -10,7 +10,7 @@ import {UpgradeableModularAccount} from "../../../src/account/UpgradeableModular import {MultiOwnerPlugin} from "../../../src/plugins/owner/MultiOwnerPlugin.sol"; import {IEntryPoint} from "../../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../../src/interfaces/erc4337/UserOperation.sol"; -import {IPluginManager} from "../../../src/interfaces/IPluginManager.sol"; +import {FunctionReference, IPluginManager} from "../../../src/interfaces/IPluginManager.sol"; import {IStandardExecutor, Call} from "../../../src/interfaces/IStandardExecutor.sol"; import { IPlugin, @@ -20,7 +20,7 @@ import { ManifestAssociatedFunctionType, ManifestAssociatedFunction } from "../../../src/interfaces/IPlugin.sol"; -import {FunctionReference, FunctionReferenceLib} from "../../../src/libraries/FunctionReferenceLib.sol"; +import {FunctionReferenceLib} from "../../../src/libraries/FunctionReferenceLib.sol"; import {MultiOwnerMSCAFactory} from "../../../src/factory/MultiOwnerMSCAFactory.sol"; import {AccountStateMutatingPlugin} from "../../mocks/plugins/AccountStateMutatingPlugin.sol"; diff --git a/test/libraries/FunctionReferenceLib.t.sol b/test/libraries/FunctionReferenceLib.t.sol index b019504e..074980dc 100644 --- a/test/libraries/FunctionReferenceLib.t.sol +++ b/test/libraries/FunctionReferenceLib.t.sol @@ -2,9 +2,12 @@ pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; -import {FunctionReference, FunctionReferenceLib} from "../../src/libraries/FunctionReferenceLib.sol"; +import {FunctionReference} from "../../src/interfaces/IPluginManager.sol"; +import {FunctionReferenceLib} from "../../src/libraries/FunctionReferenceLib.sol"; contract FunctionReferenceLibTest is Test { + using FunctionReferenceLib for FunctionReference; + function testFuzz_functionReference_packing(address addr, uint8 functionId) public { // console.log("addr: ", addr); // console.log("functionId: ", vm.toString(functionId)); @@ -18,19 +21,19 @@ contract FunctionReferenceLibTest is Test { } function testFuzz_functionReference_operators(FunctionReference a, FunctionReference b) public { - assertTrue(a == a); - assertTrue(b == b); + assertTrue(a.eq(a)); + assertTrue(b.eq(b)); if (FunctionReference.unwrap(a) == FunctionReference.unwrap(b)) { - assertTrue(a == b); - assertTrue(b == a); - assertFalse(a != b); - assertFalse(b != a); + assertTrue(a.eq(b)); + assertTrue(b.eq(a)); + assertFalse(a.notEq(b)); + assertFalse(b.notEq(a)); } else { - assertTrue(a != b); - assertTrue(b != a); - assertFalse(a == b); - assertFalse(b == a); + assertTrue(a.notEq(b)); + assertTrue(b.notEq(a)); + assertFalse(a.eq(b)); + assertFalse(b.eq(a)); } } } diff --git a/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol b/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol index 482a143d..23c581be 100644 --- a/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol +++ b/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol @@ -12,8 +12,8 @@ import { import {IStandardExecutor} from "../../../src/interfaces/IStandardExecutor.sol"; import {IPluginExecutor} from "../../../src/interfaces/IPluginExecutor.sol"; import {IPlugin} from "../../../src/interfaces/IPlugin.sol"; +import {FunctionReference} from "../../../src/interfaces/IPluginManager.sol"; import {BaseTestPlugin} from "./BaseTestPlugin.sol"; -import {FunctionReference} from "../../../src/libraries/FunctionReferenceLib.sol"; import {ResultCreatorPlugin} from "./ReturnDataPluginMocks.sol"; import {Counter} from "../Counter.sol"; diff --git a/test/mocks/plugins/ManifestValidityMocks.sol b/test/mocks/plugins/ManifestValidityMocks.sol index 474ff01c..33b5d67f 100644 --- a/test/mocks/plugins/ManifestValidityMocks.sol +++ b/test/mocks/plugins/ManifestValidityMocks.sol @@ -12,8 +12,8 @@ import { import {IStandardExecutor} from "../../../src/interfaces/IStandardExecutor.sol"; import {IPluginExecutor} from "../../../src/interfaces/IPluginExecutor.sol"; import {IPlugin} from "../../../src/interfaces/IPlugin.sol"; +import {FunctionReference} from "../../../src/interfaces/IPluginManager.sol"; import {BaseTestPlugin} from "./BaseTestPlugin.sol"; -import {FunctionReference} from "../../../src/libraries/FunctionReferenceLib.sol"; contract BadValidationMagicValue_UserOp_Plugin is BaseTestPlugin { function onInstall(bytes calldata) external override {} diff --git a/test/mocks/plugins/ReturnDataPluginMocks.sol b/test/mocks/plugins/ReturnDataPluginMocks.sol index 05148cf9..1c695a2e 100644 --- a/test/mocks/plugins/ReturnDataPluginMocks.sol +++ b/test/mocks/plugins/ReturnDataPluginMocks.sol @@ -11,8 +11,8 @@ import { import {IStandardExecutor} from "../../../src/interfaces/IStandardExecutor.sol"; import {IPluginExecutor} from "../../../src/interfaces/IPluginExecutor.sol"; import {IPlugin} from "../../../src/interfaces/IPlugin.sol"; +import {FunctionReference} from "../../../src/interfaces/IPluginManager.sol"; import {BaseTestPlugin} from "./BaseTestPlugin.sol"; -import {FunctionReference} from "../../../src/libraries/FunctionReferenceLib.sol"; contract RegularResultContract { function foo() external pure returns (bytes32) { diff --git a/test/plugin/TokenReceiverPlugin.t.sol b/test/plugin/TokenReceiverPlugin.t.sol index ee5e8421..22b38ca7 100644 --- a/test/plugin/TokenReceiverPlugin.t.sol +++ b/test/plugin/TokenReceiverPlugin.t.sol @@ -13,9 +13,9 @@ import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Re import {TokenReceiverPlugin} from "../../src/plugins/TokenReceiverPlugin.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; +import {FunctionReference} from "../../src/interfaces/IPluginManager.sol"; import {AccountStorageV1} from "../../src/libraries/AccountStorageV1.sol"; import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; -import {FunctionReference} from "../../src/libraries/FunctionReferenceLib.sol"; import {MockERC777} from "../mocks/tokens/MockERC777.sol"; import {MockERC1155} from "../mocks/tokens/MockERC1155.sol"; diff --git a/test/plugin/owner/MultiOwnerPluginIntegration.t.sol b/test/plugin/owner/MultiOwnerPluginIntegration.t.sol index 85ddd6d1..b086f181 100644 --- a/test/plugin/owner/MultiOwnerPluginIntegration.t.sol +++ b/test/plugin/owner/MultiOwnerPluginIntegration.t.sol @@ -11,11 +11,11 @@ import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol"; import {UpgradeableModularAccount} from "../../../src/account/UpgradeableModularAccount.sol"; import {IEntryPoint} from "../../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../../src/interfaces/erc4337/UserOperation.sol"; +import {FunctionReference} from "../../../src/interfaces/IPluginManager.sol"; import {MultiOwnerPlugin} from "../../../src/plugins/owner/MultiOwnerPlugin.sol"; import {IMultiOwnerPlugin} from "../../../src/plugins/owner/IMultiOwnerPlugin.sol"; import {Utils} from "../../Utils.sol"; import {Call} from "../../../src/interfaces/IStandardExecutor.sol"; -import {FunctionReference} from "../../../src/libraries/FunctionReferenceLib.sol"; import {Counter} from "../../mocks/Counter.sol"; import {MultiOwnerMSCAFactory} from "../../../src/factory/MultiOwnerMSCAFactory.sol"; diff --git a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol index 61cf172d..120302c5 100644 --- a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol +++ b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol @@ -16,7 +16,8 @@ import {ISessionKeyPermissionsUpdates} from import {SessionKeyPlugin} from "../../../src/plugins/session/SessionKeyPlugin.sol"; import {IEntryPoint} from "../../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../../src/interfaces/erc4337/UserOperation.sol"; -import {FunctionReference, FunctionReferenceLib} from "../../../src/libraries/FunctionReferenceLib.sol"; +import {FunctionReference} from "../../../src/interfaces/IPluginManager.sol"; +import {FunctionReferenceLib} from "../../../src/libraries/FunctionReferenceLib.sol"; import {Call} from "../../../src/interfaces/IStandardExecutor.sol"; import {MultiOwnerMSCAFactory} from "../../../src/factory/MultiOwnerMSCAFactory.sol"; diff --git a/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol b/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol index e84072d6..3ab74a37 100644 --- a/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol @@ -15,8 +15,9 @@ import {ISessionKeyPermissionsUpdates} from "../../../../src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol"; import {IEntryPoint} from "../../../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../../../src/interfaces/erc4337/UserOperation.sol"; -import {FunctionReference, FunctionReferenceLib} from "../../../../src/libraries/FunctionReferenceLib.sol"; import {Call} from "../../../../src/interfaces/IStandardExecutor.sol"; +import {FunctionReference} from "../../../../src/interfaces/IPluginManager.sol"; +import {FunctionReferenceLib} from "../../../../src/libraries/FunctionReferenceLib.sol"; import {MultiOwnerMSCAFactory} from "../../../../src/factory/MultiOwnerMSCAFactory.sol"; import {MockERC20} from "../../../mocks/tokens/MockERC20.sol"; diff --git a/test/plugin/session/permissions/SessionKeyGasLimits.t.sol b/test/plugin/session/permissions/SessionKeyGasLimits.t.sol index b2f92d0c..d11bdfeb 100644 --- a/test/plugin/session/permissions/SessionKeyGasLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyGasLimits.t.sol @@ -15,8 +15,9 @@ import {ISessionKeyPermissionsUpdates} from "../../../../src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol"; import {IEntryPoint} from "../../../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../../../src/interfaces/erc4337/UserOperation.sol"; +import {FunctionReference} from "../../../../src/interfaces/IPluginManager.sol"; import {Call} from "../../../../src/interfaces/IStandardExecutor.sol"; -import {FunctionReference, FunctionReferenceLib} from "../../../../src/libraries/FunctionReferenceLib.sol"; +import {FunctionReferenceLib} from "../../../../src/libraries/FunctionReferenceLib.sol"; import {MultiOwnerMSCAFactory} from "../../../../src/factory/MultiOwnerMSCAFactory.sol"; diff --git a/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol b/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol index 097c4c45..b2dd72ab 100644 --- a/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol @@ -15,8 +15,9 @@ import {ISessionKeyPermissionsUpdates} from "../../../../src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol"; import {IEntryPoint} from "../../../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../../../src/interfaces/erc4337/UserOperation.sol"; -import {FunctionReference, FunctionReferenceLib} from "../../../../src/libraries/FunctionReferenceLib.sol"; import {Call} from "../../../../src/interfaces/IStandardExecutor.sol"; +import {FunctionReference} from "../../../../src/interfaces/IPluginManager.sol"; +import {FunctionReferenceLib} from "../../../../src/libraries/FunctionReferenceLib.sol"; import {MultiOwnerMSCAFactory} from "../../../../src/factory/MultiOwnerMSCAFactory.sol"; diff --git a/test/plugin/session/permissions/SessionKeyPermissions.t.sol b/test/plugin/session/permissions/SessionKeyPermissions.t.sol index 628446ad..162b7f68 100644 --- a/test/plugin/session/permissions/SessionKeyPermissions.t.sol +++ b/test/plugin/session/permissions/SessionKeyPermissions.t.sol @@ -15,8 +15,9 @@ import {ISessionKeyPermissionsUpdates} from "../../../../src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol"; import {IEntryPoint} from "../../../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../../../src/interfaces/erc4337/UserOperation.sol"; -import {FunctionReference, FunctionReferenceLib} from "../../../../src/libraries/FunctionReferenceLib.sol"; import {Call} from "../../../../src/interfaces/IStandardExecutor.sol"; +import {FunctionReference} from "../../../../src/interfaces/IPluginManager.sol"; +import {FunctionReferenceLib} from "../../../../src/libraries/FunctionReferenceLib.sol"; import {Counter} from "../../../mocks/Counter.sol"; import {MultiOwnerMSCAFactory} from "../../../../src/factory/MultiOwnerMSCAFactory.sol"; From 851c6cff52f1ddb1691bff904371d31a08d195e8 Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Mon, 22 Jan 2024 16:58:15 -0800 Subject: [PATCH 084/106] fix: [quantstamp-29] add comments for castlib functions (#101) --- src/libraries/CastLib.sol | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libraries/CastLib.sol b/src/libraries/CastLib.sol index 19cd9fe9..d942cbaf 100644 --- a/src/libraries/CastLib.sol +++ b/src/libraries/CastLib.sol @@ -8,6 +8,8 @@ import {SetValue} from "./LinkedListSetUtils.sol"; /// @author Alchemy /// @notice Library for various data type conversions. library CastLib { + /// @dev Input array is not verified. If called with non FunctionReference type array input, return data will + /// be incorrect. function toFunctionReferenceArray(SetValue[] memory vals) internal pure @@ -18,6 +20,7 @@ library CastLib { } } + /// @dev Input array is not verified. If used with non address type array input, return data will be incorrect. function toAddressArray(SetValue[] memory values) internal pure returns (address[] memory addresses) { bytes32[] memory valuesBytes; From f258b31a1364be8779a382f48ab120a19e23fa86 Mon Sep 17 00:00:00 2001 From: David Philipson Date: Mon, 22 Jan 2024 22:17:12 -0500 Subject: [PATCH 085/106] fix: [quantstamp-16] Disallow using dependencies for hooks (#77) --- src/account/PluginManagerInternals.sol | 33 +- src/interfaces/IPlugin.sol | 2 +- test/account/AccountExecHooks.t.sol | 1003 ++---------------- test/account/AccountPreValidationHooks.t.sol | 239 ++--- 4 files changed, 205 insertions(+), 1072 deletions(-) diff --git a/src/account/PluginManagerInternals.sol b/src/account/PluginManagerInternals.sol index b27d3e56..d93a6883 100644 --- a/src/account/PluginManagerInternals.sol +++ b/src/account/PluginManagerInternals.sol @@ -386,6 +386,9 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { ); } + // Passed to _resolveManifestFunction when DEPENDENCY is not a valid function type. + FunctionReference[] memory noDependencies = new FunctionReference[](0); + // Add pre user operation validation hooks length = manifest.preUserOpValidationHooks.length; for (uint256 i = 0; i < length; ++i) { @@ -395,7 +398,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { _resolveManifestFunction( mh.associatedFunction, plugin, - dependencies, + noDependencies, ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY ) ); @@ -410,7 +413,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { _resolveManifestFunction( mh.associatedFunction, plugin, - dependencies, + noDependencies, ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY ) ); @@ -423,10 +426,10 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { _addExecHooks( mh.executionSelector, _resolveManifestFunction( - mh.preExecHook, plugin, dependencies, ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY + mh.preExecHook, plugin, noDependencies, ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY ), _resolveManifestFunction( - mh.postExecHook, plugin, dependencies, ManifestAssociatedFunctionType.NONE + mh.postExecHook, plugin, noDependencies, ManifestAssociatedFunctionType.NONE ) ); } @@ -496,6 +499,9 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { // Remove components according to the manifest, in reverse order (by component type) of their installation. + // Passed to _resolveManifestFunction when DEPENDENCY is not a valid function type. + FunctionReference[] memory noDependencies = new FunctionReference[](0); + // Remove pre and post execution function hooks length = args.manifest.executionHooks.length; for (uint256 i = 0; i < length; ++i) { @@ -503,10 +509,13 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { _removeExecHooks( mh.executionSelector, _resolveManifestFunction( - mh.preExecHook, args.plugin, dependencies, ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY + mh.preExecHook, + args.plugin, + noDependencies, + ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY ), _resolveManifestFunction( - mh.postExecHook, args.plugin, dependencies, ManifestAssociatedFunctionType.NONE + mh.postExecHook, args.plugin, noDependencies, ManifestAssociatedFunctionType.NONE ) ); } @@ -521,7 +530,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { _resolveManifestFunction( mh.associatedFunction, args.plugin, - dependencies, + noDependencies, ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY ) ); @@ -537,7 +546,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { _resolveManifestFunction( mh.associatedFunction, args.plugin, - dependencies, + noDependencies, ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY ) ); @@ -630,6 +639,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { function _resolveManifestFunction( ManifestFunction memory manifestFunction, address plugin, + // Can be empty to indicate that type DEPENDENCY is invalid for this function. FunctionReference[] memory dependencies, // Indicates which magic value, if any, is permissible for the function to resolve. ManifestAssociatedFunctionType allowedMagicValue @@ -637,7 +647,12 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { if (manifestFunction.functionType == ManifestAssociatedFunctionType.SELF) { return FunctionReferenceLib.pack(plugin, manifestFunction.functionId); } else if (manifestFunction.functionType == ManifestAssociatedFunctionType.DEPENDENCY) { - return dependencies[manifestFunction.dependencyIndex]; + uint256 index = manifestFunction.dependencyIndex; + if (index < dependencies.length) { + return dependencies[manifestFunction.dependencyIndex]; + } else { + revert InvalidPluginManifest(); + } } else if (manifestFunction.functionType == ManifestAssociatedFunctionType.RUNTIME_VALIDATION_ALWAYS_ALLOW) { if (allowedMagicValue == ManifestAssociatedFunctionType.RUNTIME_VALIDATION_ALWAYS_ALLOW) { diff --git a/src/interfaces/IPlugin.sol b/src/interfaces/IPlugin.sol index 62b3831b..1d6e292d 100644 --- a/src/interfaces/IPlugin.sol +++ b/src/interfaces/IPlugin.sol @@ -56,7 +56,7 @@ struct ManifestExternalCallPermission { struct PluginManifest { // List of ERC-165 interfaceIds to add to account to support introspection checks. bytes4[] interfaceIds; - // If this plugin depends on other plugins' validation functions and/or hooks, the interface IDs of + // If this plugin depends on other plugins' validation functions, the interface IDs of // those plugins MUST be provided here, with its position in the array matching the `dependencyIndex` // members of `ManifestFunction` structs used in the manifest. bytes4[] dependencyInterfaceIds; diff --git a/test/account/AccountExecHooks.t.sol b/test/account/AccountExecHooks.t.sol index f9ca99bb..4d15b98d 100644 --- a/test/account/AccountExecHooks.t.sol +++ b/test/account/AccountExecHooks.t.sol @@ -6,6 +6,7 @@ import {Test} from "forge-std/Test.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; +import {PluginManagerInternals} from "../../src/account/PluginManagerInternals.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; @@ -49,8 +50,6 @@ contract UpgradeableModularAccountExecHooksTest is Test { event PluginInstalled(address indexed plugin, bytes32 manifestHash, FunctionReference[] dependencies); event PluginUninstalled(address indexed plugin, bool indexed callbacksSucceeded); - // emitted by MockPlugin - event ReceivedCall(bytes msgData, uint256 msgValue); function setUp() public { entryPoint = IEntryPoint(address(new EntryPoint())); @@ -113,8 +112,8 @@ contract UpgradeableModularAccountExecHooksTest is Test { vm.startPrank(owner1); - vm.expectEmit(true, true, true, true); - emit ReceivedCall( + vm.expectCall( + address(mockPlugin1), abi.encodeWithSelector( IPlugin.preExecutionHook.selector, _PRE_HOOK_FUNCTION_ID_1, @@ -122,7 +121,7 @@ contract UpgradeableModularAccountExecHooksTest is Test { 0, // msg.value in call to account abi.encodeWithSelector(_EXEC_SELECTOR) ), - 0 // msg value in call to plugin + 1 ); (bool success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); @@ -191,147 +190,92 @@ contract UpgradeableModularAccountExecHooksTest is Test { vm.stopPrank(); } - /// @dev Plugin 1 hook pair: [1, 2] - /// Expected execution: [1, 2] - function test_execHookPair_install() public { + function test_overlappingPreExecHookAlwaysDeny_install() public { vm.startPrank(owner1); _installPlugin1WithHooks( _EXEC_SELECTOR, ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _PRE_HOOK_FUNCTION_ID_1, + functionType: ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY, + functionId: 0, dependencyIndex: 0 }), + ManifestFunction({functionType: ManifestAssociatedFunctionType.NONE, functionId: 0, dependencyIndex: 0}) + ); + + _installPlugin2WithHooks( + _EXEC_SELECTOR, ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _POST_HOOK_FUNCTION_ID_2, + functionType: ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY, + functionId: 0, dependencyIndex: 0 - }) + }), + ManifestFunction({functionType: ManifestAssociatedFunctionType.NONE, functionId: 0, dependencyIndex: 0}), + new FunctionReference[](0) ); vm.stopPrank(); } - /// @dev Plugin 1 hook pair: [1, 2] - /// Expected execution: [1, 2] - function test_execHookPair_run() public { - test_execHookPair_install(); + function test_overlappingPreExecHookAlwaysDeny_revert() public { + test_overlappingPreExecHookAlwaysDeny_install(); vm.startPrank(owner1); - vm.expectEmit(true, true, true, true); - // pre hook call - emit ReceivedCall( - abi.encodeWithSelector( - IPlugin.preExecutionHook.selector, - _PRE_HOOK_FUNCTION_ID_1, - owner1, // caller - 0, // msg.value in call to account - abi.encodeWithSelector(_EXEC_SELECTOR) - ), - 0 // msg value in call to plugin - ); - vm.expectEmit(true, true, true, true); - // exec call - emit ReceivedCall(abi.encodePacked(_EXEC_SELECTOR), 0); - vm.expectEmit(true, true, true, true); - // post hook call - emit ReceivedCall( - abi.encodeCall( - IPlugin.postExecutionHook, (_POST_HOOK_FUNCTION_ID_2, abi.encode(_PRE_HOOK_FUNCTION_ID_1)) - ), - 0 // msg value in call to plugin - ); - - (bool success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); - assertTrue(success); + (bool success, bytes memory returnData) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); + assertFalse(success); + assertEq(returnData, abi.encodeWithSelector(UpgradeableModularAccount.AlwaysDenyRule.selector)); vm.stopPrank(); } - /// @dev Plugin 1 hook pair: [1, 2] - /// Expected execution: [1, 2] - function test_execHookPair_uninstall() public { - test_execHookPair_install(); + function test_overlappingPreExecHookAlwaysDeny_uninstallPlugin1() public { + test_overlappingPreExecHookAlwaysDeny_install(); vm.startPrank(owner1); _uninstallPlugin(mockPlugin1); - vm.stopPrank(); - } - - /// @dev Plugin 1 hook pair: [null, 2] - /// Expected execution: [null, 2] - function test_postOnlyExecHook_install() public { - vm.startPrank(owner1); - - _installPlugin1WithHooks( - _EXEC_SELECTOR, - ManifestFunction({functionType: ManifestAssociatedFunctionType.NONE, functionId: 0, dependencyIndex: 0}), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _POST_HOOK_FUNCTION_ID_2, - dependencyIndex: 0 - }) - ); - - vm.stopPrank(); - } - - /// @dev Plugin 1 hook pair: [null, 2] - /// Expected execution: [null, 2] - function test_postOnlyExecHook_run() public { - test_postOnlyExecHook_install(); - - vm.startPrank(owner1); - - vm.expectEmit(true, true, true, true); - emit ReceivedCall( - abi.encodeCall(IPlugin.postExecutionHook, (_POST_HOOK_FUNCTION_ID_2, "")), - 0 // msg value in call to plugin - ); - + // Execution selector should no longer exist. (bool success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); - assertTrue(success); + assertFalse(success); vm.stopPrank(); } - /// @dev Plugin 1 hook pair: [null, 2] - /// Expected execution: [null, 2] - function test_postOnlyExecHook_uninstall() public { - test_postOnlyExecHook_install(); + function test_overlappingPreExecHookAlwaysDeny_uninstallPlugin2() public { + test_overlappingPreExecHookAlwaysDeny_install(); vm.startPrank(owner1); - _uninstallPlugin(mockPlugin1); + _uninstallPlugin(mockPlugin2); + + (bool success, bytes memory returnData) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); + assertFalse(success); + assertEq(returnData, abi.encodeWithSelector(UpgradeableModularAccount.AlwaysDenyRule.selector)); vm.stopPrank(); } - /// @dev Plugin 1 hook pair: [1, null] - /// Plugin 2 hook pair: [1, null] - /// Expected execution: [1, null] - function test_overlappingPreExecHooks_install() public { + /// Plugins cannot depend on hooks from other plugins. + function test_overlappingPreExecHook_invalid() public { vm.startPrank(owner1); - // Install the first plugin. _installPlugin1WithHooks( _EXEC_SELECTOR, ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _PRE_HOOK_FUNCTION_ID_1, + functionType: ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY, + functionId: 0, dependencyIndex: 0 }), ManifestFunction({functionType: ManifestAssociatedFunctionType.NONE, functionId: 0, dependencyIndex: 0}) ); - // Install a second plugin that applies the first plugin's hook to the same selector. FunctionReference[] memory dependencies = new FunctionReference[](1); - dependencies[0] = FunctionReferenceLib.pack(address(mockPlugin1), _PRE_HOOK_FUNCTION_ID_1); - _installPlugin2WithHooks( + dependencies[0] = FunctionReferenceLib.pack(address(mockPlugin1), 1); + + vm.expectRevert(abi.encodeWithSelector(PluginManagerInternals.InvalidPluginManifest.selector)); + this.installPlugin2WithHooksNoSuccessCheck( _EXEC_SELECTOR, ManifestFunction({ functionType: ManifestAssociatedFunctionType.DEPENDENCY, @@ -341,80 +285,13 @@ contract UpgradeableModularAccountExecHooksTest is Test { ManifestFunction({functionType: ManifestAssociatedFunctionType.NONE, functionId: 0, dependencyIndex: 0}), dependencies ); - - vm.stopPrank(); - } - - /// @dev Plugin 1 hook pair: [1, null] - /// Plugin 2 hook pair: [1, null] - /// Expected execution: [1, null] - function test_overlappingPreExecHooks_run() public { - test_overlappingPreExecHooks_install(); - - vm.startPrank(owner1); - - // Expect the pre hook to be called just once. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.preExecutionHook.selector, - _PRE_HOOK_FUNCTION_ID_1, - owner1, // caller - 0, // msg.value in call to account - abi.encodeWithSelector(_EXEC_SELECTOR) - ), - 1 - ); - - (bool success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); - assertTrue(success); - - vm.stopPrank(); - } - - /// @dev Plugin 1 hook pair: [1, null] - /// Plugin 2 hook pair: [1, null] - /// Expected execution: [1, null] - function test_overlappingPreExecHooks_uninstall() public { - test_overlappingPreExecHooks_install(); - - vm.startPrank(owner1); - - // Uninstall the second plugin. - _uninstallPlugin(mockPlugin2); - - // Expect the pre hook to still exist after uninstalling a plugin with a duplicate hook. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.preExecutionHook.selector, - _PRE_HOOK_FUNCTION_ID_1, - owner1, // caller - 0, // msg.value in call to account - abi.encodeWithSelector(_EXEC_SELECTOR) - ), - 1 - ); - (bool success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); - assertTrue(success); - - // Uninstall the first plugin. - _uninstallPlugin(mockPlugin1); - - // Execution selector should no longer exist. - (success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); - assertFalse(success); - - vm.stopPrank(); } /// @dev Plugin 1 hook pair: [1, 2] - /// Plugin 2 hook pair: [1, 2] /// Expected execution: [1, 2] - function test_overlappingExecHookPairs_install() public { + function test_execHookPair_install() public { vm.startPrank(owner1); - // Install the first plugin. _installPlugin1WithHooks( _EXEC_SELECTOR, ManifestFunction({ @@ -429,37 +306,16 @@ contract UpgradeableModularAccountExecHooksTest is Test { }) ); - // Install a second plugin that applies the first plugin's hook pair to the same selector. - FunctionReference[] memory dependencies = new FunctionReference[](2); - dependencies[0] = FunctionReferenceLib.pack(address(mockPlugin1), _PRE_HOOK_FUNCTION_ID_1); - dependencies[1] = FunctionReferenceLib.pack(address(mockPlugin1), _POST_HOOK_FUNCTION_ID_2); - _installPlugin2WithHooks( - _EXEC_SELECTOR, - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.DEPENDENCY, - functionId: 0, - dependencyIndex: 0 - }), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.DEPENDENCY, - functionId: 0, - dependencyIndex: 1 - }), - dependencies - ); - vm.stopPrank(); } /// @dev Plugin 1 hook pair: [1, 2] - /// Plugin 2 hook pair: [1, 2] /// Expected execution: [1, 2] - function test_overlappingExecHookPairs_run() public { - test_overlappingExecHookPairs_install(); + function test_execHookPair_run() public { + test_execHookPair_install(); vm.startPrank(owner1); - // Expect the pre hook to be called just once. vm.expectCall( address(mockPlugin1), abi.encodeWithSelector( @@ -471,14 +327,11 @@ contract UpgradeableModularAccountExecHooksTest is Test { ), 1 ); - - // Expect the post hook to be called just once, with the expected data. + vm.expectCall(address(mockPlugin1), abi.encodePacked(_EXEC_SELECTOR), 1); vm.expectCall( address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_2, - abi.encode(_PRE_HOOK_FUNCTION_ID_1) // preExecHookData + abi.encodeCall( + IPlugin.postExecutionHook, (_POST_HOOK_FUNCTION_ID_2, abi.encode(_PRE_HOOK_FUNCTION_ID_1)) ), 1 ); @@ -490,64 +343,25 @@ contract UpgradeableModularAccountExecHooksTest is Test { } /// @dev Plugin 1 hook pair: [1, 2] - /// Plugin 2 hook pair: [1, 2] /// Expected execution: [1, 2] - function test_overlappingExecHookPairs_uninstall() public { - test_overlappingExecHookPairs_install(); + function test_execHookPair_uninstall() public { + test_execHookPair_install(); vm.startPrank(owner1); - // Uninstall the second plugin. - _uninstallPlugin(mockPlugin2); - - // Expect the pre/post hooks to still exist after uninstalling a plugin with a duplicate hook. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.preExecutionHook.selector, - _PRE_HOOK_FUNCTION_ID_1, - owner1, // caller - 0, // msg.value in call to account - abi.encodeWithSelector(_EXEC_SELECTOR) - ), - 1 - ); - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_2, - abi.encode(_PRE_HOOK_FUNCTION_ID_1) // preExecHookData - ), - 1 - ); - (bool success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); - assertTrue(success); - - // Uninstall the first plugin. _uninstallPlugin(mockPlugin1); - // Execution selector should no longer exist. - (success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); - assertFalse(success); - vm.stopPrank(); } - /// @dev Plugin 1 hook pair: [1, 2] - /// Plugin 2 hook pair: [3, 2] - /// Expected execution: [1, 2], [3, 2] - function test_overlappingExecHookPairsOnPost_install() public { + /// @dev Plugin 1 hook pair: [null, 2] + /// Expected execution: [null, 2] + function test_postOnlyExecHook_install() public { vm.startPrank(owner1); - // Install the first plugin. _installPlugin1WithHooks( _EXEC_SELECTOR, - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _PRE_HOOK_FUNCTION_ID_1, - dependencyIndex: 0 - }), + ManifestFunction({functionType: ManifestAssociatedFunctionType.NONE, functionId: 0, dependencyIndex: 0}), ManifestFunction({ functionType: ManifestAssociatedFunctionType.SELF, functionId: _POST_HOOK_FUNCTION_ID_2, @@ -555,689 +369,34 @@ contract UpgradeableModularAccountExecHooksTest is Test { }) ); - // Install the second plugin. - FunctionReference[] memory dependencies = new FunctionReference[](1); - dependencies[0] = FunctionReferenceLib.pack(address(mockPlugin1), _POST_HOOK_FUNCTION_ID_2); - _installPlugin2WithHooks( - _EXEC_SELECTOR, - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _PRE_HOOK_FUNCTION_ID_3, - dependencyIndex: 0 - }), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.DEPENDENCY, - functionId: 0, - dependencyIndex: 0 - }), - dependencies - ); - vm.stopPrank(); } - /// @dev Plugin 1 hook pair: [1, 2] - /// Plugin 2 hook pair: [3, 2] - /// Expected execution: [1, 2], [3, 2] - function test_overlappingExecHookPairsOnPost_run() public { - test_overlappingExecHookPairsOnPost_install(); + /// @dev Plugin 1 hook pair: [null, 2] + /// Expected execution: [null, 2] + function test_postOnlyExecHook_run() public { + test_postOnlyExecHook_install(); vm.startPrank(owner1); - // Expect each pre hook to be called once. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.preExecutionHook.selector, - _PRE_HOOK_FUNCTION_ID_1, - owner1, // caller - 0, // msg.value in call to account - abi.encodeWithSelector(_EXEC_SELECTOR) - ), - 1 - ); vm.expectCall( - address(mockPlugin2), - abi.encodeWithSelector( - IPlugin.preExecutionHook.selector, - _PRE_HOOK_FUNCTION_ID_3, - owner1, // caller - 0, // msg.value in call to account - abi.encodeWithSelector(_EXEC_SELECTOR) - ), - 1 + address(mockPlugin1), abi.encodeCall(IPlugin.postExecutionHook, (_POST_HOOK_FUNCTION_ID_2, "")), 1 ); - - // Expect the post hook to be called twice, with the expected data. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_2, - abi.encode(_PRE_HOOK_FUNCTION_ID_1) // preExecHookData - ), - 1 - ); - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_2, - abi.encode(_PRE_HOOK_FUNCTION_ID_3) // preExecHookData - ), - 1 - ); - - (bool success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); - assertTrue(success); - - vm.stopPrank(); - } - - /// @dev Plugin 1 hook pair: [1, 2] - /// Plugin 2 hook pair: [3, 2] - /// Expected execution: [1, 2], [3, 2] - function test_overlappingExecHookPairsOnPost_uninstall() public { - test_overlappingExecHookPairsOnPost_install(); - - vm.startPrank(owner1); - - // Uninstall the second plugin. - _uninstallPlugin(mockPlugin2); - - // Expect the pre/post hooks to still exist after uninstalling a plugin with a duplicate hook. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.preExecutionHook.selector, - _PRE_HOOK_FUNCTION_ID_1, - owner1, // caller - 0, // msg.value in call to account - abi.encodeWithSelector(_EXEC_SELECTOR) - ), - 1 - ); - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_2, - abi.encode(_PRE_HOOK_FUNCTION_ID_1) // preExecHookData - ), - 1 - ); - (bool success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); - assertTrue(success); - - // Uninstall the first plugin. - _uninstallPlugin(mockPlugin1); - - // Execution selector should no longer exist. - (success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); - assertFalse(success); - - vm.stopPrank(); - } - - /// @dev Plugin 1 hook pair: [1, 2] - /// Plugin 2 hook pair: [1, 4] - /// Expected execution: [1, 2], [1, 4] - function test_overlappingExecHookPairsOnPre_install() public { - vm.startPrank(owner1); - - // Install the first plugin. - _installPlugin1WithHooks( - _EXEC_SELECTOR, - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _PRE_HOOK_FUNCTION_ID_1, - dependencyIndex: 0 - }), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _POST_HOOK_FUNCTION_ID_2, - dependencyIndex: 0 - }) - ); - - // Install the second plugin. - FunctionReference[] memory dependencies = new FunctionReference[](1); - dependencies[0] = FunctionReferenceLib.pack(address(mockPlugin1), _PRE_HOOK_FUNCTION_ID_1); - _installPlugin2WithHooks( - _EXEC_SELECTOR, - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.DEPENDENCY, - functionId: 0, - dependencyIndex: 0 - }), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _POST_HOOK_FUNCTION_ID_4, - dependencyIndex: 0 - }), - dependencies - ); - - vm.stopPrank(); - } - - /// @dev Plugin 1 hook pair: [1, 2] - /// Plugin 2 hook pair: [1, 4] - /// Expected execution: [1, 2], [null, 4] - function test_overlappingExecHookPairsOnPre_run() public { - test_overlappingExecHookPairsOnPre_install(); - - vm.startPrank(owner1); - - // Expect the pre hook to be called twice, each passing data over to their respective post hooks. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.preExecutionHook.selector, - _PRE_HOOK_FUNCTION_ID_1, - owner1, // caller - 0, // msg.value in call to account - abi.encodeWithSelector(_EXEC_SELECTOR) - ), - 1 - ); - - // Expect each post hook to be called once, with the expected data. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_2, - abi.encode(_PRE_HOOK_FUNCTION_ID_1) // preExecHookData - ), - 1 - ); - vm.expectCall( - address(mockPlugin2), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_4, - abi.encode(_PRE_HOOK_FUNCTION_ID_1) // preExecHookData - ), - 1 - ); - - (bool success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); - assertTrue(success); - - vm.stopPrank(); - } - - /// @dev Plugin 1 hook pair: [1, 2] - /// Plugin 2 hook pair: [1, 4] - /// Expected execution: [1, 2], [1, 4] - function test_overlappingExecHookPairsOnPre_uninstall() public { - test_overlappingExecHookPairsOnPre_install(); - - vm.startPrank(owner1); - - // Uninstall the second plugin. - _uninstallPlugin(mockPlugin2); - - // Expect the pre/post hooks to still exist after uninstalling a plugin with a duplicate hook. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.preExecutionHook.selector, - _PRE_HOOK_FUNCTION_ID_1, - owner1, // caller - 0, // msg.value in call to account - abi.encodeWithSelector(_EXEC_SELECTOR) - ), - 1 - ); - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_2, - abi.encode(_PRE_HOOK_FUNCTION_ID_1) // preExecHookData - ), - 1 - ); - (bool success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); - assertTrue(success); - - // Uninstall the first plugin. - _uninstallPlugin(mockPlugin1); - - // Execution selector should no longer exist. - (success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); - assertFalse(success); - - vm.stopPrank(); - } - - /// @dev Plugin 1 hook pair: [1, 2] - /// Plugin 2 hook pair: [1, null] - /// Expected execution: [1, 2] - function test_overlappingExecHookPairsOnPreWithNullPost_install() public { - vm.startPrank(owner1); - - // Install the first plugin. - _installPlugin1WithHooks( - _EXEC_SELECTOR, - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _PRE_HOOK_FUNCTION_ID_1, - dependencyIndex: 0 - }), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _POST_HOOK_FUNCTION_ID_2, - dependencyIndex: 0 - }) - ); - - // Install the second plugin. - FunctionReference[] memory dependencies = new FunctionReference[](1); - dependencies[0] = FunctionReferenceLib.pack(address(mockPlugin1), _PRE_HOOK_FUNCTION_ID_1); - _installPlugin2WithHooks( - _EXEC_SELECTOR, - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.DEPENDENCY, - functionId: 0, - dependencyIndex: 0 - }), - ManifestFunction({functionType: ManifestAssociatedFunctionType.NONE, functionId: 0, dependencyIndex: 0}), - dependencies - ); - - vm.stopPrank(); - } - - /// @dev Plugin 1 hook pair: [1, 2] - /// Plugin 2 hook pair: [1, null] - /// Expected execution: [1, 2] - function test_overlappingExecHookPairsOnPreWithNullPost_run() public { - test_overlappingExecHookPairsOnPreWithNullPost_install(); - - vm.startPrank(owner1); - - // Expect the pre hook to be called just once. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.preExecutionHook.selector, - _PRE_HOOK_FUNCTION_ID_1, - owner1, // caller - 0, // msg.value in call to account - abi.encodeWithSelector(_EXEC_SELECTOR) - ), - 1 - ); - - // Expect the post hook to be called just once, with the expected data. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_2, - abi.encode(_PRE_HOOK_FUNCTION_ID_1) // preExecHookData - ), - 1 - ); - - (bool success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); - assertTrue(success); - - vm.stopPrank(); - } - - /// @dev Plugin 1 hook pair: [1, 2] - /// Plugin 2 hook pair: [1, null] - /// Expected execution: [1, 2] - function test_overlappingExecHookPairsOnPreWithNullPost_uninstall() public { - test_overlappingExecHookPairsOnPreWithNullPost_install(); - - vm.startPrank(owner1); - - // Uninstall the second plugin. - _uninstallPlugin(mockPlugin2); - - // Expect the pre/post hooks to still exist after uninstalling a plugin with a duplicate hook. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.preExecutionHook.selector, - _PRE_HOOK_FUNCTION_ID_1, - owner1, // caller - 0, // msg.value in call to account - abi.encodeWithSelector(_EXEC_SELECTOR) - ), - 1 - ); - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_2, - abi.encode(_PRE_HOOK_FUNCTION_ID_1) // preExecHookData - ), - 1 - ); - (bool success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); - assertTrue(success); - - // Uninstall the first plugin. - _uninstallPlugin(mockPlugin1); - - // Execution selector should no longer exist. - (success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); - assertFalse(success); - - vm.stopPrank(); - } - - /// @dev Plugin 1 hook pair: [1, 2] - /// Plugin 2 hook pair: [null, 2] - /// Expected execution: [1, 2], [null, 2] - function test_overlappingExecHookPairsOnPostWithNullPre_install() public { - vm.startPrank(owner1); - - // Install the first plugin. - _installPlugin1WithHooks( - _EXEC_SELECTOR, - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _PRE_HOOK_FUNCTION_ID_1, - dependencyIndex: 0 - }), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _POST_HOOK_FUNCTION_ID_2, - dependencyIndex: 0 - }) - ); - - // Install the second plugin. - FunctionReference[] memory dependencies = new FunctionReference[](1); - dependencies[0] = FunctionReferenceLib.pack(address(mockPlugin1), _POST_HOOK_FUNCTION_ID_2); - _installPlugin2WithHooks( - _EXEC_SELECTOR, - ManifestFunction({functionType: ManifestAssociatedFunctionType.NONE, functionId: 0, dependencyIndex: 0}), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.DEPENDENCY, - functionId: 0, - dependencyIndex: 0 - }), - dependencies - ); - - vm.stopPrank(); - } - - /// @dev Plugin 1 hook pair: [1, 2] - /// Plugin 2 hook pair: [null, 2] - /// Expected execution: [1, 2], [null, 2] - function test_overlappingExecHookPairsOnPostWithNullPre_run() public { - test_overlappingExecHookPairsOnPostWithNullPre_install(); - - vm.startPrank(owner1); - - // Expect the pre hook to be called just once. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.preExecutionHook.selector, - _PRE_HOOK_FUNCTION_ID_1, - owner1, // caller - 0, // msg.value in call to account - abi.encodeWithSelector(_EXEC_SELECTOR) - ), - 1 - ); - - // Expect the post hook to be called twice, with the expected data. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_2, - abi.encode(_PRE_HOOK_FUNCTION_ID_1) // preExecHookData - ), - 1 - ); - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_2, - "" // preExecHookData - ), - 1 - ); - - (bool success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); - assertTrue(success); - - vm.stopPrank(); - } - - /// @dev Plugin 1 hook pair: [1, 2] - /// Plugin 2 hook pair: [null, 2] - /// Expected execution: [1, 2], [null, 2] - function test_overlappingExecHookPairsOnPostWithNullPre_uninstall() public { - test_overlappingExecHookPairsOnPostWithNullPre_install(); - - vm.startPrank(owner1); - - // Uninstall the second plugin. - _uninstallPlugin(mockPlugin2); - - // Expect the pre/post hooks to still exist after uninstalling a plugin with a duplicate hook. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.preExecutionHook.selector, - _PRE_HOOK_FUNCTION_ID_1, - owner1, // caller - 0, // msg.value in call to account - abi.encodeWithSelector(_EXEC_SELECTOR) - ), - 1 - ); - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_2, - abi.encode(_PRE_HOOK_FUNCTION_ID_1) // preExecHookData - ), - 1 - ); - (bool success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); - assertTrue(success); - - // Uninstall the first plugin. - _uninstallPlugin(mockPlugin1); - - // Execution selector should no longer exist. - (success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); - assertFalse(success); + (bool success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); + assertTrue(success); vm.stopPrank(); } /// @dev Plugin 1 hook pair: [null, 2] - /// Plugin 2 hook pair: [null, 2] /// Expected execution: [null, 2] - function test_overlappingPostExecHooks_install() public { - vm.startPrank(owner1); - - // Install the first plugin. - _installPlugin1WithHooks( - _EXEC_SELECTOR, - ManifestFunction({functionType: ManifestAssociatedFunctionType.NONE, functionId: 0, dependencyIndex: 0}), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _POST_HOOK_FUNCTION_ID_2, - dependencyIndex: 0 - }) - ); - - // Install the second plugin. - FunctionReference[] memory dependencies = new FunctionReference[](1); - dependencies[0] = FunctionReferenceLib.pack(address(mockPlugin1), _POST_HOOK_FUNCTION_ID_2); - _installPlugin2WithHooks( - _EXEC_SELECTOR, - ManifestFunction({functionType: ManifestAssociatedFunctionType.NONE, functionId: 0, dependencyIndex: 0}), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.DEPENDENCY, - functionId: 0, - dependencyIndex: 0 - }), - dependencies - ); - - vm.stopPrank(); - } - - /// @dev Plugin 1 hook pair: [null, 2] - /// Plugin 2 hook pair: [null, 2] - /// Expected execution: [null, 2] - function test_overlappingPostExecHooks_run() public { - test_overlappingPostExecHooks_install(); - - vm.startPrank(owner1); - - // Expect the post hook to be called just once. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_2, - "" // preExecHookData - ), - 1 - ); - - (bool success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); - assertTrue(success); - - vm.stopPrank(); - } - - /// @dev Plugin 1 hook pair: [null, 2] - /// Plugin 2 hook pair: [null, 2] - /// Expected execution: [null, 2] - function test_overlappingPostExecHooks_uninstall() public { - test_overlappingPostExecHooks_install(); + function test_postOnlyExecHook_uninstall() public { + test_postOnlyExecHook_install(); vm.startPrank(owner1); - // Uninstall the second plugin. - _uninstallPlugin(mockPlugin2); - - // Expect the pre/post hooks to still exist after uninstalling a plugin with a duplicate hook. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_2, - "" // preExecHookData - ), - 1 - ); - (bool success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); - assertTrue(success); - - // Uninstall the first plugin. _uninstallPlugin(mockPlugin1); - // Execution selector should no longer exist. - (success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); - assertFalse(success); - - vm.stopPrank(); - } - - /// @dev Plugin 1 hook pair: [1, 2] - /// Plugin 2 hook pair: [null, 2] - /// Expected execution: [1, 2], [null, 2] - function test_execHooksWithPostOnlyForNativeFunction_install() public { - vm.startPrank(owner1); - - // Install the first plugin. - _installPlugin1WithHooks( - UpgradeableModularAccount.execute.selector, - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _PRE_HOOK_FUNCTION_ID_1, - dependencyIndex: 0 - }), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.SELF, - functionId: _POST_HOOK_FUNCTION_ID_2, - dependencyIndex: 0 - }) - ); - - // Install the second plugin. - FunctionReference[] memory dependencies = new FunctionReference[](1); - dependencies[0] = FunctionReferenceLib.pack(address(mockPlugin1), _POST_HOOK_FUNCTION_ID_2); - _installPlugin2WithHooks( - UpgradeableModularAccount.execute.selector, - ManifestFunction({functionType: ManifestAssociatedFunctionType.NONE, functionId: 0, dependencyIndex: 0}), - ManifestFunction({ - functionType: ManifestAssociatedFunctionType.DEPENDENCY, - functionId: 0, - dependencyIndex: 0 - }), - dependencies - ); - - vm.stopPrank(); - } - - /// @dev Plugin 1 hook pair: [1, 2] - /// Plugin 2 hook pair: [null, 2] - /// Expected execution: [1, 2], [null, 2] - function test_execHooksWithPostOnlyForNativeFunction_run() public { - test_execHooksWithPostOnlyForNativeFunction_install(); - - vm.startPrank(owner1); - - // Expect the pre hook to be called just once. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.preExecutionHook.selector, - _PRE_HOOK_FUNCTION_ID_1, - owner1, // caller - 0, // msg.value in call to account - abi.encodeWithSelector(UpgradeableModularAccount.execute.selector, address(0), 0, "") - ), - 1 - ); - - // Expect the post hook to be called twice, with the expected data. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_2, - abi.encode(_PRE_HOOK_FUNCTION_ID_1) // preExecHookData - ), - 1 - ); - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.postExecutionHook.selector, - _POST_HOOK_FUNCTION_ID_2, - "" // preExecHookData - ), - 1 - ); - - account1.execute(address(0), 0, ""); - vm.stopPrank(); } @@ -1250,8 +409,7 @@ contract UpgradeableModularAccountExecHooksTest is Test { mockPlugin1 = new MockPlugin(m1); manifestHash1 = keccak256(abi.encode(mockPlugin1.pluginManifest())); - vm.expectEmit(true, true, true, true); - emit ReceivedCall(abi.encodeCall(IPlugin.onInstall, (bytes(""))), 0); + vm.expectCall(address(mockPlugin1), abi.encodeCall(IPlugin.onInstall, (bytes(""))), 1); vm.expectEmit(true, true, true, true); emit PluginInstalled(address(mockPlugin1), manifestHash1, new FunctionReference[](0)); @@ -1271,6 +429,27 @@ contract UpgradeableModularAccountExecHooksTest is Test { ManifestFunction memory postHook, FunctionReference[] memory dependencies ) internal { + _installPlugin2WithHooksInternal(selector, preHook, postHook, dependencies, true); + } + + function installPlugin2WithHooksNoSuccessCheck( + bytes4 selector, + ManifestFunction memory preHook, + ManifestFunction memory postHook, + FunctionReference[] memory dependencies + ) external { + _installPlugin2WithHooksInternal(selector, preHook, postHook, dependencies, false); + } + + function _installPlugin2WithHooksInternal( + bytes4 selector, + ManifestFunction memory preHook, + ManifestFunction memory postHook, + FunctionReference[] memory dependencies, + bool expectSuccess + ) internal { + vm.startPrank(owner1); + if (preHook.functionType == ManifestAssociatedFunctionType.DEPENDENCY) { m2.dependencyInterfaceIds.push(type(IPlugin).interfaceId); } @@ -1283,10 +462,11 @@ contract UpgradeableModularAccountExecHooksTest is Test { mockPlugin2 = new MockPlugin(m2); manifestHash2 = keccak256(abi.encode(mockPlugin2.pluginManifest())); - vm.expectEmit(true, true, true, true); - emit ReceivedCall(abi.encodeCall(IPlugin.onInstall, (bytes(""))), 0); - vm.expectEmit(true, true, true, true); - emit PluginInstalled(address(mockPlugin2), manifestHash2, dependencies); + if (expectSuccess) { + vm.expectCall(address(mockPlugin2), abi.encodeCall(IPlugin.onInstall, (bytes(""))), 1); + vm.expectEmit(true, true, true, true); + emit PluginInstalled(address(mockPlugin2), manifestHash2, dependencies); + } account1.installPlugin({ plugin: address(mockPlugin2), @@ -1294,11 +474,12 @@ contract UpgradeableModularAccountExecHooksTest is Test { pluginInitData: bytes(""), dependencies: dependencies }); + + vm.stopPrank(); } function _uninstallPlugin(MockPlugin plugin) internal { - vm.expectEmit(true, true, true, true); - emit ReceivedCall(abi.encodeCall(IPlugin.onUninstall, (bytes(""))), 0); + vm.expectCall(address(plugin), abi.encodeCall(IPlugin.onUninstall, (bytes(""))), 1); vm.expectEmit(true, true, true, true); emit PluginUninstalled(address(plugin), true); diff --git a/test/account/AccountPreValidationHooks.t.sol b/test/account/AccountPreValidationHooks.t.sol index a461498c..f045defe 100644 --- a/test/account/AccountPreValidationHooks.t.sol +++ b/test/account/AccountPreValidationHooks.t.sol @@ -6,6 +6,7 @@ import {Test} from "forge-std/Test.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; +import {PluginManagerInternals} from "../../src/account/PluginManagerInternals.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {IMultiOwnerPlugin} from "../../src/plugins/owner/IMultiOwnerPlugin.sol"; import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; @@ -243,20 +244,24 @@ contract UpgradeableModularAccountPreValidationHooksTest is Test { vm.stopPrank(); } - /// @dev Plugin 1 hook: 1 - /// Plugin 2 hook: 1 - /// Expected execution: [1] - function test_overlappingPreRuntimeValidationHook_install() public { + /// Plugins cannot depend on hooks from other plugins. + function test_overlappingPreRuntimeValidationHook_invalid() public { vm.startPrank(owner1); _installPlugin1WithPreRuntimeValidationHook( _EXEC_SELECTOR, - ManifestFunction({functionType: ManifestAssociatedFunctionType.SELF, functionId: 1, dependencyIndex: 0}) + ManifestFunction({ + functionType: ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY, + functionId: 0, + dependencyIndex: 0 + }) ); FunctionReference[] memory dependencies = new FunctionReference[](1); dependencies[0] = FunctionReferenceLib.pack(address(mockPlugin1), 1); - _installPlugin2WithPreRuntimeValidationHook( + + vm.expectRevert(abi.encodeWithSelector(PluginManagerInternals.InvalidPluginManifest.selector)); + this.installPlugin2WithpreRuntimeValidationHookNoSuccessCheck( _EXEC_SELECTOR, ManifestFunction({ functionType: ManifestAssociatedFunctionType.DEPENDENCY, @@ -265,69 +270,69 @@ contract UpgradeableModularAccountPreValidationHooksTest is Test { }), dependencies ); - - vm.stopPrank(); } - /// @dev Plugin 1 hook: 1 - /// Plugin 2 hook: 1 - /// Expected execution: [1] - function test_overlappingPreRuntimeValidationHooks_run() public { - test_overlappingPreRuntimeValidationHook_install(); - + function test_overlappingPreRuntimeValidationHookAlwaysDeny_install() public { vm.startPrank(owner1); - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.preRuntimeValidationHook.selector, - 1, - owner1, // caller - 0, // msg.value in call to account - abi.encodeWithSelector(_EXEC_SELECTOR) - ), - 1 + _installPlugin1WithPreRuntimeValidationHook( + _EXEC_SELECTOR, + ManifestFunction({ + functionType: ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY, + functionId: 0, + dependencyIndex: 0 + }) ); - (bool success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); - assertTrue(success); + _installPlugin2WithPreRuntimeValidationHook( + _EXEC_SELECTOR, + ManifestFunction({ + functionType: ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY, + functionId: 0, + dependencyIndex: 0 + }), + new FunctionReference[](0) + ); vm.stopPrank(); } - /// @dev Plugin 1 hook: 1 - /// Plugin 2 hook: 1 - /// Expected execution: [1] - function test_overlappingPreRuntimeValidationHook_uninstall() public { - test_overlappingPreRuntimeValidationHook_install(); + function test_overlappingPreRuntimeValidationHookAlwaysDeny_revert() public { + test_overlappingPreRuntimeValidationHookAlwaysDeny_install(); vm.startPrank(owner1); - // Uninstall the second plugin. - _uninstallPlugin(mockPlugin2); + (bool success, bytes memory returnData) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); + assertFalse(success); + assertEq(returnData, abi.encodeWithSelector(UpgradeableModularAccount.AlwaysDenyRule.selector)); - // Expect the hook to still exist after uninstalling a plugin with a duplicate hook. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector( - IPlugin.preRuntimeValidationHook.selector, - 1, - owner1, // caller - 0, // msg.value in call to account - abi.encodeWithSelector(_EXEC_SELECTOR) - ), - 1 - ); + vm.stopPrank(); + } - (bool success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); - assertTrue(success); + function test_overlappingPreRuntimeValidationHookAlwaysDeny_uninstallPlugin1() public { + test_overlappingPreRuntimeValidationHookAlwaysDeny_install(); + + vm.startPrank(owner1); - // Uninstall the first plugin. _uninstallPlugin(mockPlugin1); // Execution selector should no longer exist. - (success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); + (bool success,) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); + assertFalse(success); + + vm.stopPrank(); + } + + function test_overlappingPreRuntimeValidationHookAlwaysDeny_uninstallPlugin2() public { + test_overlappingPreRuntimeValidationHookAlwaysDeny_install(); + + vm.startPrank(owner1); + + _uninstallPlugin(mockPlugin2); + + (bool success, bytes memory returnData) = address(account1).call(abi.encodeWithSelector(_EXEC_SELECTOR)); assertFalse(success); + assertEq(returnData, abi.encodeWithSelector(UpgradeableModularAccount.AlwaysDenyRule.selector)); vm.stopPrank(); } @@ -486,121 +491,30 @@ contract UpgradeableModularAccountPreValidationHooksTest is Test { vm.startPrank(address(entryPoint)); vm.expectRevert(UpgradeableModularAccount.AlwaysDenyRule.selector); account1.validateUserOp(userOp, userOpHash, 0); - vm.stopPrank(); } - /// @dev Plugin 1 hook: 1 - /// Plugin 2 hook: 1 - /// Expected execution: [1, 1] - function test_overlappingPreUserOpValidationHooks_install() public { + function test_overlappingPreUserOpValidationHookAlwaysDeny_install() public { vm.startPrank(owner1); _installPlugin1WithPreUserOpValidationHook( _EXEC_SELECTOR, - ManifestFunction({functionType: ManifestAssociatedFunctionType.SELF, functionId: 1, dependencyIndex: 0}) + ManifestFunction({ + functionType: ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY, + functionId: 0, + dependencyIndex: 0 + }) ); - FunctionReference[] memory dependencies = new FunctionReference[](1); - dependencies[0] = FunctionReferenceLib.pack(address(mockPlugin1), 1); _installPlugin2WithPreUserOpValidationHook( _EXEC_SELECTOR, ManifestFunction({ - functionType: ManifestAssociatedFunctionType.DEPENDENCY, + functionType: ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY, functionId: 0, dependencyIndex: 0 }), - dependencies - ); - - vm.stopPrank(); - } - - /// @dev Plugin 1 hook: 1 - /// Plugin 2 hook: 1 - /// Expected execution: [1] - function test_overlappingPreUserOpValidationHooks_run() public { - test_overlappingPreUserOpValidationHooks_install(); - - vm.startPrank(owner1); - - UserOperation memory userOp = UserOperation({ - sender: address(account1), - nonce: 0, - initCode: "", - callData: abi.encodeWithSelector(_EXEC_SELECTOR), - callGasLimit: CALL_GAS_LIMIT, - verificationGasLimit: VERIFICATION_GAS_LIMIT, - preVerificationGas: 0, - maxFeePerGas: 2, - maxPriorityFeePerGas: 1, - paymasterAndData: "", - signature: "" - }); - - // Generate signature - bytes32 userOpHash = entryPoint.getUserOpHash(userOp); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(owner1Key, userOpHash.toEthSignedMessageHash()); - userOp.signature = abi.encodePacked(r, s, v); - - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector(IPlugin.preUserOpValidationHook.selector, 1, userOp, userOpHash), - 1 - ); - - UserOperation[] memory userOps = new UserOperation[](1); - userOps[0] = userOp; - - entryPoint.handleOps(userOps, beneficiary); - - vm.stopPrank(); - } - - /// @dev Plugin 1 hook: 1 - /// Plugin 2 hook: 1 - /// Expected execution: [1] - function test_overlappingPreUserOpValidationHooks_uninstall() public { - test_overlappingPreUserOpValidationHooks_install(); - - vm.startPrank(owner1); - - // Uninstall the second plugin. - _uninstallPlugin(mockPlugin2); - - UserOperation memory userOp = UserOperation({ - sender: address(account1), - nonce: 0, - initCode: "", - callData: abi.encodeWithSelector(_EXEC_SELECTOR), - callGasLimit: CALL_GAS_LIMIT, - verificationGasLimit: VERIFICATION_GAS_LIMIT, - preVerificationGas: 0, - maxFeePerGas: 2, - maxPriorityFeePerGas: 1, - paymasterAndData: "", - signature: "" - }); - - // Generate signature - bytes32 userOpHash = entryPoint.getUserOpHash(userOp); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(owner1Key, userOpHash.toEthSignedMessageHash()); - userOp.signature = abi.encodePacked(r, s, v); - - // Expect hook 1 to still exist. - vm.expectCall( - address(mockPlugin1), - abi.encodeWithSelector(IPlugin.preUserOpValidationHook.selector, 1, userOp, userOpHash), - 1 + new FunctionReference[](0) ); - UserOperation[] memory userOps = new UserOperation[](1); - userOps[0] = userOp; - - entryPoint.handleOps(userOps, beneficiary); - - // Uninstall the first plugin. - _uninstallPlugin(mockPlugin1); - vm.stopPrank(); } @@ -632,6 +546,25 @@ contract UpgradeableModularAccountPreValidationHooksTest is Test { ManifestFunction memory hook, FunctionReference[] memory dependencies ) internal { + _installPlugin2WithPreRuntimeValidationHookInternal(selector, hook, dependencies, true); + } + + function installPlugin2WithpreRuntimeValidationHookNoSuccessCheck( + bytes4 selector, + ManifestFunction memory hook, + FunctionReference[] memory dependencies + ) external { + _installPlugin2WithPreRuntimeValidationHookInternal(selector, hook, dependencies, false); + } + + function _installPlugin2WithPreRuntimeValidationHookInternal( + bytes4 selector, + ManifestFunction memory hook, + FunctionReference[] memory dependencies, + bool expectSuccess + ) internal { + vm.startPrank(owner1); + if (hook.functionType == ManifestAssociatedFunctionType.DEPENDENCY) { m2.dependencyInterfaceIds.push(type(IPlugin).interfaceId); } @@ -640,9 +573,11 @@ contract UpgradeableModularAccountPreValidationHooksTest is Test { mockPlugin2 = new MockPlugin(m2); manifestHash2 = keccak256(abi.encode(mockPlugin2.pluginManifest())); - vm.expectCall(address(mockPlugin2), abi.encodeCall(IPlugin.onInstall, ("")), 1); - vm.expectEmit(true, true, true, true); - emit PluginInstalled(address(mockPlugin2), manifestHash2, dependencies); + if (expectSuccess) { + vm.expectCall(address(mockPlugin2), abi.encodeCall(IPlugin.onInstall, ("")), 1); + vm.expectEmit(true, true, true, true); + emit PluginInstalled(address(mockPlugin2), manifestHash2, dependencies); + } account1.installPlugin({ plugin: address(mockPlugin2), @@ -650,6 +585,8 @@ contract UpgradeableModularAccountPreValidationHooksTest is Test { pluginInitData: bytes(""), dependencies: dependencies }); + + vm.stopPrank(); } function _installPlugin1WithPreUserOpValidationHook(bytes4 selector, ManifestFunction memory hook) internal { From 66377e3b824192c1fd7a4fe911be923583abc5d5 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Mon, 22 Jan 2024 22:43:03 -0500 Subject: [PATCH 086/106] fix: [spearbit-24] refactor _resolveManifestFunction (#97) --- src/account/PluginManagerInternals.sol | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/account/PluginManagerInternals.sol b/src/account/PluginManagerInternals.sol index d93a6883..b19cee5b 100644 --- a/src/account/PluginManagerInternals.sol +++ b/src/account/PluginManagerInternals.sol @@ -646,26 +646,25 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { ) internal pure returns (FunctionReference) { if (manifestFunction.functionType == ManifestAssociatedFunctionType.SELF) { return FunctionReferenceLib.pack(plugin, manifestFunction.functionId); - } else if (manifestFunction.functionType == ManifestAssociatedFunctionType.DEPENDENCY) { + } + if (manifestFunction.functionType == ManifestAssociatedFunctionType.DEPENDENCY) { uint256 index = manifestFunction.dependencyIndex; if (index < dependencies.length) { - return dependencies[manifestFunction.dependencyIndex]; - } else { - revert InvalidPluginManifest(); + return dependencies[index]; } - } else if (manifestFunction.functionType == ManifestAssociatedFunctionType.RUNTIME_VALIDATION_ALWAYS_ALLOW) - { + revert InvalidPluginManifest(); + } + if (manifestFunction.functionType == ManifestAssociatedFunctionType.RUNTIME_VALIDATION_ALWAYS_ALLOW) { if (allowedMagicValue == ManifestAssociatedFunctionType.RUNTIME_VALIDATION_ALWAYS_ALLOW) { return FunctionReferenceLib._RUNTIME_VALIDATION_ALWAYS_ALLOW; - } else { - revert InvalidPluginManifest(); } - } else if (manifestFunction.functionType == ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY) { + revert InvalidPluginManifest(); + } + if (manifestFunction.functionType == ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY) { if (allowedMagicValue == ManifestAssociatedFunctionType.PRE_HOOK_ALWAYS_DENY) { return FunctionReferenceLib._PRE_HOOK_ALWAYS_DENY; - } else { - revert InvalidPluginManifest(); } + revert InvalidPluginManifest(); } return FunctionReferenceLib._EMPTY_FUNCTION_REFERENCE; // Empty checks are done elsewhere } From 65547fca10f90f249127d20ef912a3f685c2367f Mon Sep 17 00:00:00 2001 From: howy <132113803+howydev@users.noreply.github.com> Date: Mon, 22 Jan 2024 23:27:01 -0500 Subject: [PATCH 087/106] fix: [spearbit-88] move SIG_VALIDATION_X to a global var (#73) Co-authored-by: Jay Paik --- src/account/UpgradeableModularAccount.sol | 1 - src/helpers/ValidationDataHelpers.sol | 5 +++- src/libraries/AssociatedLinkedListSetLib.sol | 2 +- src/libraries/CastLib.sol | 2 +- .../{LinkedListSetUtils.sol => Constants.sol} | 4 +++ src/libraries/CountableLinkedListSetLib.sol | 2 +- src/libraries/LinkedListSetLib.sol | 2 +- src/plugins/owner/MultiOwnerPlugin.sol | 13 +++------ src/plugins/session/SessionKeyPlugin.sol | 9 +++--- .../permissions/SessionKeyPermissions.sol | 5 ++-- test/account/ValidationIntersection.t.sol | 29 +++++++++---------- .../AssociatedLinkedListSetHandler.sol | 2 +- .../handlers/LinkedListSetHandler.sol | 2 +- .../AssociatedLinkedListSetLib.t.sol | 2 +- .../libraries/CountableLinkedListSetLib.t.sol | 2 +- test/libraries/LinkedListSetLib.t.sol | 2 +- 16 files changed, 42 insertions(+), 42 deletions(-) rename src/libraries/{LinkedListSetUtils.sol => Constants.sol} (78%) diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index ce9a4a9c..f96642c1 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -65,7 +65,6 @@ contract UpgradeableModularAccount is } IEntryPoint private immutable _ENTRY_POINT; - uint256 internal constant _SIG_VALIDATION_FAILED = 1; // As per the EIP-165 spec, no interface should ever match 0xffffffff bytes4 internal constant _INTERFACE_ID_INVALID = 0xffffffff; diff --git a/src/helpers/ValidationDataHelpers.sol b/src/helpers/ValidationDataHelpers.sol index e3c9bd2d..ba9a3630 100644 --- a/src/helpers/ValidationDataHelpers.sol +++ b/src/helpers/ValidationDataHelpers.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.22; +import {SIG_VALIDATION_FAILED} from "../libraries/Constants.sol"; + /// @dev This helper function assumes that uint160(validationData1) and uint160(validationData2) can only be 0 or 1 // solhint-disable-next-line private-vars-leading-underscore function _coalescePreValidation(uint256 validationData1, uint256 validationData2) @@ -47,5 +49,6 @@ function _coalesceValidation(uint256 preValidationData, uint256 validationData) resValidationData |= ((validAfter1 < validAfter2) ? uint256(validAfter2) << 208 : uint256(validAfter1) << 208); // If prevalidation failed, bubble up failure - resValidationData |= uint160(preValidationData) == 1 ? 1 : uint160(validationData); + resValidationData |= + uint160(preValidationData) == SIG_VALIDATION_FAILED ? SIG_VALIDATION_FAILED : uint160(validationData); } diff --git a/src/libraries/AssociatedLinkedListSetLib.sol b/src/libraries/AssociatedLinkedListSetLib.sol index b0532b2b..a2e7ee6a 100644 --- a/src/libraries/AssociatedLinkedListSetLib.sol +++ b/src/libraries/AssociatedLinkedListSetLib.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.22; -import {SetValue, SENTINEL_VALUE, HAS_NEXT_FLAG} from "./LinkedListSetUtils.sol"; +import {SetValue, SENTINEL_VALUE, HAS_NEXT_FLAG} from "./Constants.sol"; /// @dev Type representing the set, which is just a storage slot placeholder like the solidity mapping type. struct AssociatedLinkedListSet { diff --git a/src/libraries/CastLib.sol b/src/libraries/CastLib.sol index d942cbaf..31f01c73 100644 --- a/src/libraries/CastLib.sol +++ b/src/libraries/CastLib.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.22; import {FunctionReference} from "./FunctionReferenceLib.sol"; -import {SetValue} from "./LinkedListSetUtils.sol"; +import {SetValue} from "./Constants.sol"; /// @title Cast Library /// @author Alchemy diff --git a/src/libraries/LinkedListSetUtils.sol b/src/libraries/Constants.sol similarity index 78% rename from src/libraries/LinkedListSetUtils.sol rename to src/libraries/Constants.sol index 7560ec0d..59159d4a 100644 --- a/src/libraries/LinkedListSetUtils.sol +++ b/src/libraries/Constants.sol @@ -9,3 +9,7 @@ bytes32 constant SENTINEL_VALUE = bytes32(uint256(1)); /// @dev Removing the last element will result in this flag not being set correctly, but all operations will /// function normally, albeit with one extra sload for getAll. bytes32 constant HAS_NEXT_FLAG = bytes32(uint256(2)); + +/// @dev As defined by ERC-4337. +uint256 constant SIG_VALIDATION_PASSED = 0; +uint256 constant SIG_VALIDATION_FAILED = 1; diff --git a/src/libraries/CountableLinkedListSetLib.sol b/src/libraries/CountableLinkedListSetLib.sol index 43d70fc5..b04c7ff0 100644 --- a/src/libraries/CountableLinkedListSetLib.sol +++ b/src/libraries/CountableLinkedListSetLib.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.22; import {LinkedListSet, LinkedListSetLib} from "./LinkedListSetLib.sol"; -import {SetValue} from "./LinkedListSetUtils.sol"; +import {SetValue} from "./Constants.sol"; /// @title Countable Linked List Set Library /// @author Alchemy diff --git a/src/libraries/LinkedListSetLib.sol b/src/libraries/LinkedListSetLib.sol index 478c6b4d..5a43190b 100644 --- a/src/libraries/LinkedListSetLib.sol +++ b/src/libraries/LinkedListSetLib.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.22; -import {SetValue, SENTINEL_VALUE, HAS_NEXT_FLAG} from "./LinkedListSetUtils.sol"; +import {SetValue, SENTINEL_VALUE, HAS_NEXT_FLAG} from "./Constants.sol"; struct LinkedListSet { // Byte Layout diff --git a/src/plugins/owner/MultiOwnerPlugin.sol b/src/plugins/owner/MultiOwnerPlugin.sol index b332a924..aa88da2f 100644 --- a/src/plugins/owner/MultiOwnerPlugin.sol +++ b/src/plugins/owner/MultiOwnerPlugin.sol @@ -24,7 +24,7 @@ import { AssociatedLinkedListSet, AssociatedLinkedListSetLib } from "../../libraries/AssociatedLinkedListSetLib.sol"; import {CastLib} from "../../libraries/CastLib.sol"; -import {SetValue} from "../../libraries/LinkedListSetUtils.sol"; +import {SetValue, SIG_VALIDATION_PASSED, SIG_VALIDATION_FAILED} from "../../libraries/Constants.sol"; /// @title Multi Owner Plugin /// @author Alchemy @@ -59,11 +59,6 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { bytes32 private constant _HASHED_VERSION = keccak256(bytes(_VERSION)); bytes32 private immutable _SALT = bytes32(bytes20(address(this))); - // ERC-4337 specific value: signature validation passed - uint256 internal constant _SIG_VALIDATION_PASSED = 0; - // ERC-4337 specific value: signature validation failed - uint256 internal constant _SIG_VALIDATION_FAILED = 1; - // bytes4(keccak256("isValidSignature(bytes32,bytes)")) bytes4 internal constant _1271_MAGIC_VALUE = 0x1626ba7e; bytes4 internal constant _1271_MAGIC_VALUE_FAILURE = 0xffffffff; @@ -183,14 +178,14 @@ contract MultiOwnerPlugin is BasePlugin, IMultiOwnerPlugin, IERC1271 { (address signer, ECDSA.RecoverError error) = userOpHash.toEthSignedMessageHash().tryRecover(userOp.signature); if (error == ECDSA.RecoverError.NoError && isOwnerOf(msg.sender, signer)) { - return _SIG_VALIDATION_PASSED; + return SIG_VALIDATION_PASSED; } if (_isValidERC1271OwnerTypeSignature(msg.sender, userOpHash, userOp.signature)) { - return _SIG_VALIDATION_PASSED; + return SIG_VALIDATION_PASSED; } - return _SIG_VALIDATION_FAILED; + return SIG_VALIDATION_FAILED; } revert NotImplemented(msg.sig, functionId); diff --git a/src/plugins/session/SessionKeyPlugin.sol b/src/plugins/session/SessionKeyPlugin.sol index 569cadfd..db703e3b 100644 --- a/src/plugins/session/SessionKeyPlugin.sol +++ b/src/plugins/session/SessionKeyPlugin.sol @@ -24,7 +24,9 @@ import { AssociatedLinkedListSet, AssociatedLinkedListSetLib } from "../../libraries/AssociatedLinkedListSetLib.sol"; import {CastLib} from "../../libraries/CastLib.sol"; -import {SetValue, SENTINEL_VALUE} from "../../libraries/LinkedListSetUtils.sol"; +import { + SetValue, SENTINEL_VALUE, SIG_VALIDATION_PASSED, SIG_VALIDATION_FAILED +} from "../../libraries/Constants.sol"; /// @title Session Key Plugin /// @author Alchemy @@ -42,9 +44,6 @@ contract SessionKeyPlugin is ISessionKeyPlugin, SessionKeyPermissions, BasePlugi string internal constant _VERSION = "1.0.0"; string internal constant _AUTHOR = "Alchemy"; - uint256 internal constant _SIG_VALIDATION_PASSED = 0; - uint256 internal constant _SIG_VALIDATION_FAILED = 1; - // Constants used in the manifest uint256 internal constant _MANIFEST_DEPENDENCY_INDEX_OWNER_USER_OP_VALIDATION = 0; uint256 internal constant _MANIFEST_DEPENDENCY_INDEX_OWNER_RUNTIME_VALIDATION = 1; @@ -177,7 +176,7 @@ contract SessionKeyPlugin is ISessionKeyPlugin, SessionKeyPermissions, BasePlugi ) { return _checkUserOpPermissions(userOp, calls, sessionKey); } - return _SIG_VALIDATION_FAILED; + return SIG_VALIDATION_FAILED; } revert InvalidSignature(sessionKey); } diff --git a/src/plugins/session/permissions/SessionKeyPermissions.sol b/src/plugins/session/permissions/SessionKeyPermissions.sol index 689400c0..bbce8120 100644 --- a/src/plugins/session/permissions/SessionKeyPermissions.sol +++ b/src/plugins/session/permissions/SessionKeyPermissions.sol @@ -10,6 +10,7 @@ import {SessionKeyPermissionsLoupe} from "./SessionKeyPermissionsLoupe.sol"; import {IStandardExecutor} from "../../../interfaces/IStandardExecutor.sol"; import {UserOperation} from "../../../interfaces/erc4337/UserOperation.sol"; +import {SIG_VALIDATION_PASSED, SIG_VALIDATION_FAILED} from "../../../libraries/Constants.sol"; /// @title Session Key Permissions /// @author Alchemy @@ -128,8 +129,8 @@ abstract contract SessionKeyPermissions is ISessionKeyPlugin, SessionKeyPermissi // otherwise a packed struct of the aggregator address (0 here), and two // 6-byte timestamps indicating the start and end times at which the op // is valid. - return uint160(validationSuccess ? 0 : 1) | (uint256(validUntil) << 160) - | (uint256(currentValidAfter) << (208)); + return uint160(validationSuccess ? SIG_VALIDATION_PASSED : SIG_VALIDATION_FAILED) + | (uint256(validUntil) << 160) | (uint256(currentValidAfter) << (208)); } /// @dev Checks permissions on a per-call basis. Should be run during user op validation once per `Call` struct diff --git a/test/account/ValidationIntersection.t.sol b/test/account/ValidationIntersection.t.sol index 3e1f192b..ced8e029 100644 --- a/test/account/ValidationIntersection.t.sol +++ b/test/account/ValidationIntersection.t.sol @@ -9,6 +9,7 @@ import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAcc import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../src/interfaces/erc4337/UserOperation.sol"; import {FunctionReference} from "../../src/interfaces/IPluginManager.sol"; +import {SIG_VALIDATION_FAILED, SIG_VALIDATION_PASSED} from "../../src/libraries/Constants.sol"; import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; @@ -20,8 +21,6 @@ import { } from "../mocks/plugins/ValidationPluginMocks.sol"; contract ValidationIntersectionTest is Test { - uint256 internal constant _SIG_VALIDATION_FAILED = 1; - IEntryPoint public entryPoint; address public owner1; @@ -92,8 +91,8 @@ contract ValidationIntersectionTest is Test { function test_validationIntersect_authorizer_sigfail_validationFunction() public { oneHookPlugin.setValidationData( - _SIG_VALIDATION_FAILED, - 0 // returns OK + SIG_VALIDATION_FAILED, + SIG_VALIDATION_PASSED // returns OK ); UserOperation memory userOp; @@ -104,13 +103,13 @@ contract ValidationIntersectionTest is Test { uint256 returnedValidationData = account1.validateUserOp(userOp, uoHash, 1 wei); // Down-cast to only check the authorizer - assertEq(uint160(returnedValidationData), _SIG_VALIDATION_FAILED); + assertEq(uint160(returnedValidationData), SIG_VALIDATION_FAILED); } function test_validationIntersect_authorizer_sigfail_hook() public { oneHookPlugin.setValidationData( - 0, // returns OK - _SIG_VALIDATION_FAILED + SIG_VALIDATION_PASSED, // returns OK + SIG_VALIDATION_FAILED ); UserOperation memory userOp; @@ -121,7 +120,7 @@ contract ValidationIntersectionTest is Test { uint256 returnedValidationData = account1.validateUserOp(userOp, uoHash, 1 wei); // Down-cast to only check the authorizer - assertEq(uint160(returnedValidationData), _SIG_VALIDATION_FAILED); + assertEq(uint160(returnedValidationData), SIG_VALIDATION_FAILED); } function test_validationIntersect_timeBounds_intersect_1() public { @@ -170,7 +169,7 @@ contract ValidationIntersectionTest is Test { address badAuthorizer = makeAddr("badAuthorizer"); oneHookPlugin.setValidationData( - 0, // returns OK + SIG_VALIDATION_PASSED, // returns OK uint256(uint160(badAuthorizer)) // returns an aggregator, which preValidation hooks are not allowed to // do. ); @@ -196,7 +195,7 @@ contract ValidationIntersectionTest is Test { oneHookPlugin.setValidationData( uint256(uint160(goodAuthorizer)), // returns a valid aggregator - 0 // returns OK + SIG_VALIDATION_PASSED // returns OK ); UserOperation memory userOp; @@ -240,7 +239,7 @@ contract ValidationIntersectionTest is Test { uint48 end2 = uint48(25); twoHookPlugin.setValidationData( - 0, // returns OK + SIG_VALIDATION_PASSED, // returns OK _packValidationData(address(0), start1, end1), _packValidationData(address(0), start2, end2) ); @@ -257,9 +256,9 @@ contract ValidationIntersectionTest is Test { function test_validationIntersect_multiplePreValidationHooksSigFail() public { twoHookPlugin.setValidationData( - 0, // returns OK - 0, // returns OK - _SIG_VALIDATION_FAILED + SIG_VALIDATION_PASSED, // returns OK + SIG_VALIDATION_PASSED, // returns OK + SIG_VALIDATION_FAILED ); UserOperation memory userOp; @@ -271,7 +270,7 @@ contract ValidationIntersectionTest is Test { uint256 returnedValidationData = account1.validateUserOp(userOp, uoHash, 1 wei); // Down-cast to only check the authorizer - assertEq(uint160(returnedValidationData), _SIG_VALIDATION_FAILED); + assertEq(uint160(returnedValidationData), SIG_VALIDATION_FAILED); } function _unpackValidationData(uint256 validationData) diff --git a/test/invariant/handlers/AssociatedLinkedListSetHandler.sol b/test/invariant/handlers/AssociatedLinkedListSetHandler.sol index 525c6f0c..d0c387d3 100644 --- a/test/invariant/handlers/AssociatedLinkedListSetHandler.sol +++ b/test/invariant/handlers/AssociatedLinkedListSetHandler.sol @@ -11,7 +11,7 @@ import { AssociatedLinkedListSet, AssociatedLinkedListSetLib } from "../../../src/libraries/AssociatedLinkedListSetLib.sol"; -import {SetValue} from "../../../src/libraries/LinkedListSetUtils.sol"; +import {SetValue} from "../../../src/libraries/Constants.sol"; /// @notice A handler contract for differential invariant testing AssociatedLinkedListSetLib /// This contract maps logic for adding, removeing, clearing, and inspecting a list diff --git a/test/invariant/handlers/LinkedListSetHandler.sol b/test/invariant/handlers/LinkedListSetHandler.sol index 77ccba2f..1468ce5f 100644 --- a/test/invariant/handlers/LinkedListSetHandler.sol +++ b/test/invariant/handlers/LinkedListSetHandler.sol @@ -8,7 +8,7 @@ import {StdUtils} from "forge-std/StdUtils.sol"; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; import {LinkedListSetLib, LinkedListSet as EnumerableSetType} from "../../../src/libraries/LinkedListSetLib.sol"; -import {SetValue} from "../../../src/libraries/LinkedListSetUtils.sol"; +import {SetValue} from "../../../src/libraries/Constants.sol"; /// @notice A handler contract for differential invariant testing LinkedListSetLib /// This contract maps logic for adding, removeing, clearing, and inspecting a list diff --git a/test/libraries/AssociatedLinkedListSetLib.t.sol b/test/libraries/AssociatedLinkedListSetLib.t.sol index 74b62f34..748081f4 100644 --- a/test/libraries/AssociatedLinkedListSetLib.t.sol +++ b/test/libraries/AssociatedLinkedListSetLib.t.sol @@ -7,7 +7,7 @@ import { AssociatedLinkedListSet, AssociatedLinkedListSetLib } from "../../src/libraries/AssociatedLinkedListSetLib.sol"; -import {SetValue, SENTINEL_VALUE} from "../../src/libraries/LinkedListSetUtils.sol"; +import {SetValue, SENTINEL_VALUE} from "../../src/libraries/Constants.sol"; contract AssociatedLinkedListSetLibTest is Test { using AssociatedLinkedListSetLib for AssociatedLinkedListSet; diff --git a/test/libraries/CountableLinkedListSetLib.t.sol b/test/libraries/CountableLinkedListSetLib.t.sol index c09deadb..b971de1a 100644 --- a/test/libraries/CountableLinkedListSetLib.t.sol +++ b/test/libraries/CountableLinkedListSetLib.t.sol @@ -5,7 +5,7 @@ import {Test} from "forge-std/Test.sol"; import {CountableLinkedListSetLib} from "../../src/libraries/CountableLinkedListSetLib.sol"; import {LinkedListSet, LinkedListSetLib} from "../../src/libraries/LinkedListSetLib.sol"; -import {SetValue} from "../../src/libraries/LinkedListSetUtils.sol"; +import {SetValue} from "../../src/libraries/Constants.sol"; contract CountableLinkedListSetLibTest is Test { using LinkedListSetLib for LinkedListSet; diff --git a/test/libraries/LinkedListSetLib.t.sol b/test/libraries/LinkedListSetLib.t.sol index 806e28ef..4373cc52 100644 --- a/test/libraries/LinkedListSetLib.t.sol +++ b/test/libraries/LinkedListSetLib.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; import {LinkedListSet, LinkedListSetLib} from "../../src/libraries/LinkedListSetLib.sol"; -import {SetValue, SENTINEL_VALUE} from "../../src/libraries/LinkedListSetUtils.sol"; +import {SetValue, SENTINEL_VALUE} from "../../src/libraries/Constants.sol"; // Ported over from test/AssociatedLinkedListSetLib.t.sol, dropping test_no_address_collision contract LinkedListSetLibTest is Test { From e4c6d977aa82d45f0c41699b6b00e3f5e19dad79 Mon Sep 17 00:00:00 2001 From: howy <132113803+howydev@users.noreply.github.com> Date: Mon, 22 Jan 2024 23:30:02 -0500 Subject: [PATCH 088/106] fix: [spearbit-106] update comments to use bitwise OR instead of logical OR operator (#64) --- src/libraries/AccountStorageV1.sol | 1 + src/libraries/AssociatedLinkedListSetLib.sol | 1 + src/plugins/session/permissions/SessionKeyPermissionsBase.sol | 1 + 3 files changed, 3 insertions(+) diff --git a/src/libraries/AccountStorageV1.sol b/src/libraries/AccountStorageV1.sol index 1fef8c9b..0cd21303 100644 --- a/src/libraries/AccountStorageV1.sol +++ b/src/libraries/AccountStorageV1.sol @@ -9,6 +9,7 @@ import {LinkedListSet} from "../libraries/LinkedListSetLib.sol"; /// @title Account Storage V1 /// @author Alchemy /// @notice Contains the storage layout for upgradeable modular accounts. +/// @dev `||` for variables in comments refers to the concat operator contract AccountStorageV1 { /// @custom:storage-location erc7201:Alchemy.UpgradeableModularAccount.Storage_V1 struct AccountStorage { diff --git a/src/libraries/AssociatedLinkedListSetLib.sol b/src/libraries/AssociatedLinkedListSetLib.sol index a2e7ee6a..2b9ace6d 100644 --- a/src/libraries/AssociatedLinkedListSetLib.sol +++ b/src/libraries/AssociatedLinkedListSetLib.sol @@ -438,6 +438,7 @@ library AssociatedLinkedListSetLib { returns (TempBytesMemory key) { // Key derivation for an entry + // Note: `||` refers to the concat operator // associated addr (left-padded) || prefix || uint224(0) batchIndex || set storage slot || entry // Word 1: // | zeros | 0x000000000000000000000000________________________________________ | diff --git a/src/plugins/session/permissions/SessionKeyPermissionsBase.sol b/src/plugins/session/permissions/SessionKeyPermissionsBase.sol index 6f37c0dc..bba2d288 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsBase.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsBase.sol @@ -82,6 +82,7 @@ abstract contract SessionKeyPermissionsBase is ISessionKeyPlugin { // The first word (32 bytes) is the associated address. // The second word (32 bytes) is the prefix and batch index concatenated. // Any subsequent words are the key data. + // Note: `||` refers to the concat operator // SessionKeyId storage key (96 bytes) // 12 padding zeros || associated address || SESSION_KEY_ID_PREFIX || batch index || 12 padding zero bytes From ac0ba48dc4faa266cc872cf7f8f07067a97e0987 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Tue, 23 Jan 2024 00:10:56 -0500 Subject: [PATCH 089/106] refactor: libraries/AccountStorageV1 -> account/AccountStorageV1 (#102) --- src/account/AccountLoupe.sol | 4 +--- src/account/AccountStorageInitializable.sol | 2 +- src/{libraries => account}/AccountStorageV1.sol | 0 src/account/PluginManagerInternals.sol | 4 +--- test/libraries/AccountStorage.t.sol | 6 ++++-- test/plugin/TokenReceiverPlugin.t.sol | 4 ++-- 6 files changed, 9 insertions(+), 11 deletions(-) rename src/{libraries => account}/AccountStorageV1.sol (100%) diff --git a/src/account/AccountLoupe.sol b/src/account/AccountLoupe.sol index ef67bcd3..2174c906 100644 --- a/src/account/AccountLoupe.sol +++ b/src/account/AccountLoupe.sol @@ -1,12 +1,10 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.22; +import {AccountStorageV1} from "../account/AccountStorageV1.sol"; import {KnownSelectors} from "../helpers/KnownSelectors.sol"; - import {IAccountLoupe} from "../interfaces/IAccountLoupe.sol"; import {FunctionReference} from "../interfaces/IPluginManager.sol"; - -import {AccountStorageV1} from "../libraries/AccountStorageV1.sol"; import {CastLib} from "../libraries/CastLib.sol"; import {CountableLinkedListSetLib} from "../libraries/CountableLinkedListSetLib.sol"; import {LinkedListSet, LinkedListSetLib} from "../libraries/LinkedListSetLib.sol"; diff --git a/src/account/AccountStorageInitializable.sol b/src/account/AccountStorageInitializable.sol index f57d3a8a..2230a989 100644 --- a/src/account/AccountStorageInitializable.sol +++ b/src/account/AccountStorageInitializable.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.22; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; -import {AccountStorageV1} from "../libraries/AccountStorageV1.sol"; +import {AccountStorageV1} from "../account/AccountStorageV1.sol"; /// @title Account Storage Initializable /// @author Alchemy diff --git a/src/libraries/AccountStorageV1.sol b/src/account/AccountStorageV1.sol similarity index 100% rename from src/libraries/AccountStorageV1.sol rename to src/account/AccountStorageV1.sol diff --git a/src/account/PluginManagerInternals.sol b/src/account/PluginManagerInternals.sol index b19cee5b..cba62402 100644 --- a/src/account/PluginManagerInternals.sol +++ b/src/account/PluginManagerInternals.sol @@ -3,8 +3,8 @@ pragma solidity ^0.8.22; import {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; +import {AccountStorageV1} from "../account/AccountStorageV1.sol"; import {KnownSelectors} from "../helpers/KnownSelectors.sol"; - import { IPlugin, ManifestAssociatedFunction, @@ -15,8 +15,6 @@ import { PluginManifest } from "../interfaces/IPlugin.sol"; import {FunctionReference, IPluginManager} from "../interfaces/IPluginManager.sol"; - -import {AccountStorageV1} from "../libraries/AccountStorageV1.sol"; import {CastLib} from "../libraries/CastLib.sol"; import {CountableLinkedListSetLib} from "../libraries/CountableLinkedListSetLib.sol"; import {FunctionReferenceLib} from "../libraries/FunctionReferenceLib.sol"; diff --git a/test/libraries/AccountStorage.t.sol b/test/libraries/AccountStorage.t.sol index 16847ae7..33091a04 100644 --- a/test/libraries/AccountStorage.t.sol +++ b/test/libraries/AccountStorage.t.sol @@ -2,10 +2,12 @@ pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; -import {AccountStorageV1} from "../../src/libraries/AccountStorageV1.sol"; + +import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; + import {AccountStorageInitializable} from "../../src/account/AccountStorageInitializable.sol"; +import {AccountStorageV1} from "../../src/account/AccountStorageV1.sol"; import {MockDiamondStorageContract} from "../mocks/MockDiamondStorageContract.sol"; -import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; // Test implementation of AccountStorageInitializable which is contained in UpgradeableModularAccount contract AccountStorageTest is Test, AccountStorageV1 { diff --git a/test/plugin/TokenReceiverPlugin.t.sol b/test/plugin/TokenReceiverPlugin.t.sol index 22b38ca7..c9b1e656 100644 --- a/test/plugin/TokenReceiverPlugin.t.sol +++ b/test/plugin/TokenReceiverPlugin.t.sol @@ -10,12 +10,12 @@ import {ERC721PresetMinterPauserAutoId} from import {IERC777Recipient} from "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol"; import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; -import {TokenReceiverPlugin} from "../../src/plugins/TokenReceiverPlugin.sol"; +import {AccountStorageV1} from "../../src/account/AccountStorageV1.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; import {FunctionReference} from "../../src/interfaces/IPluginManager.sol"; -import {AccountStorageV1} from "../../src/libraries/AccountStorageV1.sol"; import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; +import {TokenReceiverPlugin} from "../../src/plugins/TokenReceiverPlugin.sol"; import {MockERC777} from "../mocks/tokens/MockERC777.sol"; import {MockERC1155} from "../mocks/tokens/MockERC1155.sol"; From b3ecbeb5643132e476dc939dd7acff4f78a80025 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Tue, 23 Jan 2024 00:16:04 -0500 Subject: [PATCH 090/106] refactor: libraries/CastLib.sol, libraries/FunctionReference.sol -> helpers/ (#103) --- src/account/AccountLoupe.sol | 2 +- src/account/PluginManagerInternals.sol | 4 ++-- src/account/UpgradeableModularAccount.sol | 23 ++++++++----------- src/{libraries => helpers}/CastLib.sol | 2 +- .../FunctionReferenceLib.sol | 0 src/plugins/owner/MultiOwnerPlugin.sol | 12 ++++------ src/plugins/session/SessionKeyPlugin.sol | 14 +++++------ test/account/AccountExecHooks.t.sol | 11 ++++----- test/account/AccountLoupe.t.sol | 15 ++++++------ test/account/AccountPreValidationHooks.t.sol | 13 +++++------ ...gradeableModularAccountPluginManager.t.sol | 21 ++++++++--------- test/account/phases/AccountStatePhases.t.sol | 15 ++++++------ test/libraries/FunctionReferenceLib.t.sol | 3 ++- .../SessionKeyPluginWithMultiOwner.t.sol | 19 ++++++++------- .../SessionKeyERC20SpendLimits.t.sol | 19 ++++++++------- .../permissions/SessionKeyGasLimits.t.sol | 17 +++++++------- .../SessionKeyNativeTokenSpendLimits.t.sol | 19 ++++++++------- .../permissions/SessionKeyPermissions.t.sol | 19 ++++++++------- 18 files changed, 106 insertions(+), 122 deletions(-) rename src/{libraries => helpers}/CastLib.sol (96%) rename src/{libraries => helpers}/FunctionReferenceLib.sol (100%) diff --git a/src/account/AccountLoupe.sol b/src/account/AccountLoupe.sol index 2174c906..5534951a 100644 --- a/src/account/AccountLoupe.sol +++ b/src/account/AccountLoupe.sol @@ -2,10 +2,10 @@ pragma solidity ^0.8.22; import {AccountStorageV1} from "../account/AccountStorageV1.sol"; +import {CastLib} from "../helpers/CastLib.sol"; import {KnownSelectors} from "../helpers/KnownSelectors.sol"; import {IAccountLoupe} from "../interfaces/IAccountLoupe.sol"; import {FunctionReference} from "../interfaces/IPluginManager.sol"; -import {CastLib} from "../libraries/CastLib.sol"; import {CountableLinkedListSetLib} from "../libraries/CountableLinkedListSetLib.sol"; import {LinkedListSet, LinkedListSetLib} from "../libraries/LinkedListSetLib.sol"; diff --git a/src/account/PluginManagerInternals.sol b/src/account/PluginManagerInternals.sol index cba62402..d8d3585c 100644 --- a/src/account/PluginManagerInternals.sol +++ b/src/account/PluginManagerInternals.sol @@ -4,6 +4,8 @@ pragma solidity ^0.8.22; import {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; import {AccountStorageV1} from "../account/AccountStorageV1.sol"; +import {CastLib} from "../helpers/CastLib.sol"; +import {FunctionReferenceLib} from "../helpers/FunctionReferenceLib.sol"; import {KnownSelectors} from "../helpers/KnownSelectors.sol"; import { IPlugin, @@ -15,9 +17,7 @@ import { PluginManifest } from "../interfaces/IPlugin.sol"; import {FunctionReference, IPluginManager} from "../interfaces/IPluginManager.sol"; -import {CastLib} from "../libraries/CastLib.sol"; import {CountableLinkedListSetLib} from "../libraries/CountableLinkedListSetLib.sol"; -import {FunctionReferenceLib} from "../libraries/FunctionReferenceLib.sol"; import {LinkedListSet, LinkedListSetLib} from "../libraries/LinkedListSetLib.sol"; /// @title Plugin Manager Internals diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index f96642c1..5bb9488f 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -3,28 +3,25 @@ pragma solidity ^0.8.22; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -import {AccountExecutor} from "./AccountExecutor.sol"; -import {AccountLoupe} from "./AccountLoupe.sol"; -import {AccountStorageInitializable} from "./AccountStorageInitializable.sol"; -import {PluginManagerInternals} from "./PluginManagerInternals.sol"; - +import {UUPSUpgradeable} from "../../ext/UUPSUpgradeable.sol"; +import {CastLib} from "../helpers/CastLib.sol"; +import {FunctionReferenceLib} from "../helpers/FunctionReferenceLib.sol"; import {_coalescePreValidation, _coalesceValidation} from "../helpers/ValidationDataHelpers.sol"; - -import {Call, IStandardExecutor} from "../interfaces/IStandardExecutor.sol"; import {IAccount} from "../interfaces/erc4337/IAccount.sol"; +import {IEntryPoint} from "../interfaces/erc4337/IEntryPoint.sol"; +import {UserOperation} from "../interfaces/erc4337/UserOperation.sol"; import {IAccountInitializable} from "../interfaces/IAccountInitializable.sol"; import {IAccountView} from "../interfaces/IAccountView.sol"; -import {IEntryPoint} from "../interfaces/erc4337/IEntryPoint.sol"; import {IPlugin, PluginManifest} from "../interfaces/IPlugin.sol"; import {IPluginExecutor} from "../interfaces/IPluginExecutor.sol"; import {FunctionReference, IPluginManager} from "../interfaces/IPluginManager.sol"; -import {UserOperation} from "../interfaces/erc4337/UserOperation.sol"; - -import {CastLib} from "../libraries/CastLib.sol"; +import {Call, IStandardExecutor} from "../interfaces/IStandardExecutor.sol"; import {CountableLinkedListSetLib} from "../libraries/CountableLinkedListSetLib.sol"; -import {FunctionReferenceLib} from "../libraries/FunctionReferenceLib.sol"; import {LinkedListSet, LinkedListSetLib} from "../libraries/LinkedListSetLib.sol"; -import {UUPSUpgradeable} from "../../ext/UUPSUpgradeable.sol"; +import {AccountExecutor} from "./AccountExecutor.sol"; +import {AccountLoupe} from "./AccountLoupe.sol"; +import {AccountStorageInitializable} from "./AccountStorageInitializable.sol"; +import {PluginManagerInternals} from "./PluginManagerInternals.sol"; /// @title Upgradeable Modular Account /// @author Alchemy diff --git a/src/libraries/CastLib.sol b/src/helpers/CastLib.sol similarity index 96% rename from src/libraries/CastLib.sol rename to src/helpers/CastLib.sol index 31f01c73..0e424ae8 100644 --- a/src/libraries/CastLib.sol +++ b/src/helpers/CastLib.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.22; +import {SetValue} from "../libraries/Constants.sol"; import {FunctionReference} from "./FunctionReferenceLib.sol"; -import {SetValue} from "./Constants.sol"; /// @title Cast Library /// @author Alchemy diff --git a/src/libraries/FunctionReferenceLib.sol b/src/helpers/FunctionReferenceLib.sol similarity index 100% rename from src/libraries/FunctionReferenceLib.sol rename to src/helpers/FunctionReferenceLib.sol diff --git a/src/plugins/owner/MultiOwnerPlugin.sol b/src/plugins/owner/MultiOwnerPlugin.sol index aa88da2f..fe72aa58 100644 --- a/src/plugins/owner/MultiOwnerPlugin.sol +++ b/src/plugins/owner/MultiOwnerPlugin.sol @@ -1,14 +1,13 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.22; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol"; +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; -import {BasePlugin} from "../BasePlugin.sol"; -import {IMultiOwnerPlugin} from "./IMultiOwnerPlugin.sol"; import {UpgradeableModularAccount, UUPSUpgradeable} from "../../account/UpgradeableModularAccount.sol"; - +import {CastLib} from "../../helpers/CastLib.sol"; +import {UserOperation} from "../../interfaces/erc4337/UserOperation.sol"; import { ManifestAssociatedFunction, ManifestAssociatedFunctionType, @@ -18,13 +17,12 @@ import { SelectorPermission } from "../../interfaces/IPlugin.sol"; import {IStandardExecutor} from "../../interfaces/IStandardExecutor.sol"; -import {UserOperation} from "../../interfaces/erc4337/UserOperation.sol"; - import { AssociatedLinkedListSet, AssociatedLinkedListSetLib } from "../../libraries/AssociatedLinkedListSetLib.sol"; -import {CastLib} from "../../libraries/CastLib.sol"; import {SetValue, SIG_VALIDATION_PASSED, SIG_VALIDATION_FAILED} from "../../libraries/Constants.sol"; +import {BasePlugin} from "../BasePlugin.sol"; +import {IMultiOwnerPlugin} from "./IMultiOwnerPlugin.sol"; /// @title Multi Owner Plugin /// @author Alchemy diff --git a/src/plugins/session/SessionKeyPlugin.sol b/src/plugins/session/SessionKeyPlugin.sol index db703e3b..b2b28495 100644 --- a/src/plugins/session/SessionKeyPlugin.sol +++ b/src/plugins/session/SessionKeyPlugin.sol @@ -3,12 +3,9 @@ pragma solidity ^0.8.22; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {BasePlugin} from "../BasePlugin.sol"; -import {ISessionKeyPlugin} from "./ISessionKeyPlugin.sol"; -import {SessionKeyPermissions} from "./permissions/SessionKeyPermissions.sol"; - +import {CastLib} from "../../helpers/CastLib.sol"; +import {UserOperation} from "../../interfaces/erc4337/UserOperation.sol"; import {IPlugin} from "../../interfaces/IPlugin.sol"; -import {IPluginExecutor} from "../../interfaces/IPluginExecutor.sol"; import { ManifestFunction, ManifestAssociatedFunctionType, @@ -17,16 +14,17 @@ import { PluginMetadata, SelectorPermission } from "../../interfaces/IPlugin.sol"; +import {IPluginExecutor} from "../../interfaces/IPluginExecutor.sol"; import {Call, IStandardExecutor} from "../../interfaces/IStandardExecutor.sol"; -import {UserOperation} from "../../interfaces/erc4337/UserOperation.sol"; - import { AssociatedLinkedListSet, AssociatedLinkedListSetLib } from "../../libraries/AssociatedLinkedListSetLib.sol"; -import {CastLib} from "../../libraries/CastLib.sol"; import { SetValue, SENTINEL_VALUE, SIG_VALIDATION_PASSED, SIG_VALIDATION_FAILED } from "../../libraries/Constants.sol"; +import {BasePlugin} from "../BasePlugin.sol"; +import {ISessionKeyPlugin} from "./ISessionKeyPlugin.sol"; +import {SessionKeyPermissions} from "./permissions/SessionKeyPermissions.sol"; /// @title Session Key Plugin /// @author Alchemy diff --git a/test/account/AccountExecHooks.t.sol b/test/account/AccountExecHooks.t.sol index 4d15b98d..e3ae71e1 100644 --- a/test/account/AccountExecHooks.t.sol +++ b/test/account/AccountExecHooks.t.sol @@ -3,15 +3,14 @@ pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {PluginManagerInternals} from "../../src/account/PluginManagerInternals.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; -import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; +import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; +import {FunctionReferenceLib} from "../../src/helpers/FunctionReferenceLib.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; -import {FunctionReference} from "../../src/interfaces/IPluginManager.sol"; -import {FunctionReferenceLib} from "../../src/libraries/FunctionReferenceLib.sol"; import { IPlugin, ManifestExecutionHook, @@ -20,8 +19,8 @@ import { ManifestAssociatedFunctionType, ManifestAssociatedFunction } from "../../src/interfaces/IPlugin.sol"; - -import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; +import {FunctionReference} from "../../src/interfaces/IPluginManager.sol"; +import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; import {MockPlugin} from "../mocks/MockPlugin.sol"; contract UpgradeableModularAccountExecHooksTest is Test { diff --git a/test/account/AccountLoupe.t.sol b/test/account/AccountLoupe.t.sol index 5c91b26d..2b5ca631 100644 --- a/test/account/AccountLoupe.t.sol +++ b/test/account/AccountLoupe.t.sol @@ -3,27 +3,26 @@ pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; -import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; +import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; -import {IMultiOwnerPlugin} from "../../src/plugins/owner/IMultiOwnerPlugin.sol"; -import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; +import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; +import {FunctionReferenceLib} from "../../src/helpers/FunctionReferenceLib.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; +import {IAccountLoupe} from "../../src/interfaces/IAccountLoupe.sol"; import { ManifestAssociatedFunctionType, ManifestExecutionHook, ManifestFunction, PluginManifest } from "../../src/interfaces/IPlugin.sol"; -import {IAccountLoupe} from "../../src/interfaces/IAccountLoupe.sol"; import {FunctionReference, IPluginManager} from "../../src/interfaces/IPluginManager.sol"; import {IStandardExecutor} from "../../src/interfaces/IStandardExecutor.sol"; -import {FunctionReferenceLib} from "../../src/libraries/FunctionReferenceLib.sol"; - -import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; -import {ComprehensivePlugin} from "../mocks/plugins/ComprehensivePlugin.sol"; +import {IMultiOwnerPlugin} from "../../src/plugins/owner/IMultiOwnerPlugin.sol"; +import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; import {MockPlugin} from "../mocks/MockPlugin.sol"; +import {ComprehensivePlugin} from "../mocks/plugins/ComprehensivePlugin.sol"; contract AccountLoupeTest is Test { IEntryPoint public entryPoint; diff --git a/test/account/AccountPreValidationHooks.t.sol b/test/account/AccountPreValidationHooks.t.sol index f045defe..9c49e6a4 100644 --- a/test/account/AccountPreValidationHooks.t.sol +++ b/test/account/AccountPreValidationHooks.t.sol @@ -3,17 +3,15 @@ pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {PluginManagerInternals} from "../../src/account/PluginManagerInternals.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; -import {IMultiOwnerPlugin} from "../../src/plugins/owner/IMultiOwnerPlugin.sol"; -import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; +import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; +import {FunctionReferenceLib} from "../../src/helpers/FunctionReferenceLib.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../src/interfaces/erc4337/UserOperation.sol"; -import {FunctionReference} from "../../src/interfaces/IPluginManager.sol"; -import {FunctionReferenceLib} from "../../src/libraries/FunctionReferenceLib.sol"; import { IPlugin, PluginManifest, @@ -21,8 +19,9 @@ import { ManifestAssociatedFunctionType, ManifestAssociatedFunction } from "../../src/interfaces/IPlugin.sol"; - -import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; +import {FunctionReference} from "../../src/interfaces/IPluginManager.sol"; +import {IMultiOwnerPlugin} from "../../src/plugins/owner/IMultiOwnerPlugin.sol"; +import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; import {MockPlugin} from "../mocks/MockPlugin.sol"; contract UpgradeableModularAccountPreValidationHooksTest is Test { diff --git a/test/account/UpgradeableModularAccountPluginManager.t.sol b/test/account/UpgradeableModularAccountPluginManager.t.sol index 0ea579bd..e96263c1 100644 --- a/test/account/UpgradeableModularAccountPluginManager.t.sol +++ b/test/account/UpgradeableModularAccountPluginManager.t.sol @@ -3,33 +3,32 @@ pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; import {IPaymaster} from "@eth-infinitism/account-abstraction/interfaces/IPaymaster.sol"; +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {PluginManagerInternals} from "../../src/account/PluginManagerInternals.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; -import {IMultiOwnerPlugin} from "../../src/plugins/owner/IMultiOwnerPlugin.sol"; -import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; -import {SessionKeyPlugin} from "../../src/plugins/session/SessionKeyPlugin.sol"; -import {TokenReceiverPlugin} from "../../src/plugins/TokenReceiverPlugin.sol"; +import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; +import {FunctionReferenceLib} from "../../src/helpers/FunctionReferenceLib.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; -import {PluginManifest} from "../../src/interfaces/IPlugin.sol"; import {IAccountLoupe} from "../../src/interfaces/IAccountLoupe.sol"; +import {PluginManifest} from "../../src/interfaces/IPlugin.sol"; +import {IPlugin, PluginManifest} from "../../src/interfaces/IPlugin.sol"; import {FunctionReference, IPluginManager} from "../../src/interfaces/IPluginManager.sol"; import {IStandardExecutor} from "../../src/interfaces/IStandardExecutor.sol"; import {Call} from "../../src/interfaces/IStandardExecutor.sol"; -import {FunctionReferenceLib} from "../../src/libraries/FunctionReferenceLib.sol"; -import {IPlugin, PluginManifest} from "../../src/interfaces/IPlugin.sol"; - +import {IMultiOwnerPlugin} from "../../src/plugins/owner/IMultiOwnerPlugin.sol"; +import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; +import {SessionKeyPlugin} from "../../src/plugins/session/SessionKeyPlugin.sol"; +import {TokenReceiverPlugin} from "../../src/plugins/TokenReceiverPlugin.sol"; import {Counter} from "../mocks/Counter.sol"; -import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; +import {MockPlugin} from "../mocks/MockPlugin.sol"; import { CanChangeManifestPluginFactory, CanChangeManifestPlugin } from "../mocks/plugins/ChangingManifestPlugin.sol"; import {ComprehensivePlugin} from "../mocks/plugins/ComprehensivePlugin.sol"; import {UninstallErrorsPlugin} from "../mocks/plugins/UninstallErrorsPlugin.sol"; -import {MockPlugin} from "../mocks/MockPlugin.sol"; contract UpgradeableModularAccountPluginManagerTest is Test { using ECDSA for bytes32; diff --git a/test/account/phases/AccountStatePhases.t.sol b/test/account/phases/AccountStatePhases.t.sol index a0627023..eebdbf16 100644 --- a/test/account/phases/AccountStatePhases.t.sol +++ b/test/account/phases/AccountStatePhases.t.sol @@ -3,15 +3,14 @@ pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {UpgradeableModularAccount} from "../../../src/account/UpgradeableModularAccount.sol"; -import {MultiOwnerPlugin} from "../../../src/plugins/owner/MultiOwnerPlugin.sol"; +import {MultiOwnerMSCAFactory} from "../../../src/factory/MultiOwnerMSCAFactory.sol"; +import {FunctionReferenceLib} from "../../../src/helpers/FunctionReferenceLib.sol"; import {IEntryPoint} from "../../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../../src/interfaces/erc4337/UserOperation.sol"; -import {FunctionReference, IPluginManager} from "../../../src/interfaces/IPluginManager.sol"; -import {IStandardExecutor, Call} from "../../../src/interfaces/IStandardExecutor.sol"; import { IPlugin, ManifestExecutionHook, @@ -20,11 +19,11 @@ import { ManifestAssociatedFunctionType, ManifestAssociatedFunction } from "../../../src/interfaces/IPlugin.sol"; -import {FunctionReferenceLib} from "../../../src/libraries/FunctionReferenceLib.sol"; -import {MultiOwnerMSCAFactory} from "../../../src/factory/MultiOwnerMSCAFactory.sol"; - -import {AccountStateMutatingPlugin} from "../../mocks/plugins/AccountStateMutatingPlugin.sol"; +import {FunctionReference, IPluginManager} from "../../../src/interfaces/IPluginManager.sol"; +import {IStandardExecutor, Call} from "../../../src/interfaces/IStandardExecutor.sol"; +import {MultiOwnerPlugin} from "../../../src/plugins/owner/MultiOwnerPlugin.sol"; import {MockPlugin} from "../../mocks/MockPlugin.sol"; +import {AccountStateMutatingPlugin} from "../../mocks/plugins/AccountStateMutatingPlugin.sol"; // A test suite that verifies how the account caches the state of plugins. This is intended to ensure consistency // of execution flow when either hooks or plugins change installation state within a single call to the account. diff --git a/test/libraries/FunctionReferenceLib.t.sol b/test/libraries/FunctionReferenceLib.t.sol index 074980dc..e72dcb41 100644 --- a/test/libraries/FunctionReferenceLib.t.sol +++ b/test/libraries/FunctionReferenceLib.t.sol @@ -2,8 +2,9 @@ pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; + +import {FunctionReferenceLib} from "../../src/helpers/FunctionReferenceLib.sol"; import {FunctionReference} from "../../src/interfaces/IPluginManager.sol"; -import {FunctionReferenceLib} from "../../src/libraries/FunctionReferenceLib.sol"; contract FunctionReferenceLibTest is Test { using FunctionReferenceLib for FunctionReference; diff --git a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol index 120302c5..83ecd853 100644 --- a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol +++ b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol @@ -3,24 +3,23 @@ pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {UpgradeableModularAccount} from "../../../src/account/UpgradeableModularAccount.sol"; -import {BasePlugin} from "../../../src/plugins/BasePlugin.sol"; -import {IMultiOwnerPlugin} from "../../../src/plugins/owner/IMultiOwnerPlugin.sol"; -import {MultiOwnerPlugin} from "../../../src/plugins/owner/MultiOwnerPlugin.sol"; -import {ISessionKeyPlugin} from "../../../src/plugins/session/ISessionKeyPlugin.sol"; import {ISessionKeyPermissionsUpdates} from "../../../src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol"; -import {SessionKeyPlugin} from "../../../src/plugins/session/SessionKeyPlugin.sol"; +import {UpgradeableModularAccount} from "../../../src/account/UpgradeableModularAccount.sol"; +import {MultiOwnerMSCAFactory} from "../../../src/factory/MultiOwnerMSCAFactory.sol"; +import {FunctionReferenceLib} from "../../../src/helpers/FunctionReferenceLib.sol"; import {IEntryPoint} from "../../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../../src/interfaces/erc4337/UserOperation.sol"; import {FunctionReference} from "../../../src/interfaces/IPluginManager.sol"; -import {FunctionReferenceLib} from "../../../src/libraries/FunctionReferenceLib.sol"; import {Call} from "../../../src/interfaces/IStandardExecutor.sol"; - -import {MultiOwnerMSCAFactory} from "../../../src/factory/MultiOwnerMSCAFactory.sol"; +import {BasePlugin} from "../../../src/plugins/BasePlugin.sol"; +import {IMultiOwnerPlugin} from "../../../src/plugins/owner/IMultiOwnerPlugin.sol"; +import {MultiOwnerPlugin} from "../../../src/plugins/owner/MultiOwnerPlugin.sol"; +import {ISessionKeyPlugin} from "../../../src/plugins/session/ISessionKeyPlugin.sol"; +import {SessionKeyPlugin} from "../../../src/plugins/session/SessionKeyPlugin.sol"; contract SessionKeyPluginWithMultiOwnerTest is Test { using ECDSA for bytes32; diff --git a/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol b/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol index 3ab74a37..48bdf718 100644 --- a/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol @@ -3,23 +3,22 @@ pragma solidity ^0.8.22; import {Test, console} from "forge-std/Test.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {UpgradeableModularAccount} from "../../../../src/account/UpgradeableModularAccount.sol"; -import {IMultiOwnerPlugin} from "../../../../src/plugins/owner/IMultiOwnerPlugin.sol"; -import {MultiOwnerPlugin} from "../../../../src/plugins/owner/MultiOwnerPlugin.sol"; -import {ISessionKeyPlugin} from "../../../../src/plugins/session/ISessionKeyPlugin.sol"; -import {SessionKeyPlugin} from "../../../../src/plugins/session/SessionKeyPlugin.sol"; import {ISessionKeyPermissionsUpdates} from "../../../../src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol"; +import {UpgradeableModularAccount} from "../../../../src/account/UpgradeableModularAccount.sol"; +import {MultiOwnerMSCAFactory} from "../../../../src/factory/MultiOwnerMSCAFactory.sol"; +import {FunctionReferenceLib} from "../../../../src/helpers/FunctionReferenceLib.sol"; import {IEntryPoint} from "../../../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../../../src/interfaces/erc4337/UserOperation.sol"; -import {Call} from "../../../../src/interfaces/IStandardExecutor.sol"; import {FunctionReference} from "../../../../src/interfaces/IPluginManager.sol"; -import {FunctionReferenceLib} from "../../../../src/libraries/FunctionReferenceLib.sol"; - -import {MultiOwnerMSCAFactory} from "../../../../src/factory/MultiOwnerMSCAFactory.sol"; +import {Call} from "../../../../src/interfaces/IStandardExecutor.sol"; +import {IMultiOwnerPlugin} from "../../../../src/plugins/owner/IMultiOwnerPlugin.sol"; +import {MultiOwnerPlugin} from "../../../../src/plugins/owner/MultiOwnerPlugin.sol"; +import {ISessionKeyPlugin} from "../../../../src/plugins/session/ISessionKeyPlugin.sol"; +import {SessionKeyPlugin} from "../../../../src/plugins/session/SessionKeyPlugin.sol"; import {MockERC20} from "../../../mocks/tokens/MockERC20.sol"; contract SessionKeyERC20SpendLimitsTest is Test { diff --git a/test/plugin/session/permissions/SessionKeyGasLimits.t.sol b/test/plugin/session/permissions/SessionKeyGasLimits.t.sol index d11bdfeb..2f7d2b43 100644 --- a/test/plugin/session/permissions/SessionKeyGasLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyGasLimits.t.sol @@ -3,23 +3,22 @@ pragma solidity ^0.8.22; import {Test, console} from "forge-std/Test.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {UpgradeableModularAccount} from "../../../../src/account/UpgradeableModularAccount.sol"; -import {IMultiOwnerPlugin} from "../../../../src/plugins/owner/IMultiOwnerPlugin.sol"; -import {MultiOwnerPlugin} from "../../../../src/plugins/owner/MultiOwnerPlugin.sol"; -import {ISessionKeyPlugin} from "../../../../src/plugins/session/ISessionKeyPlugin.sol"; -import {SessionKeyPlugin} from "../../../../src/plugins/session/SessionKeyPlugin.sol"; import {ISessionKeyPermissionsUpdates} from "../../../../src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol"; +import {UpgradeableModularAccount} from "../../../../src/account/UpgradeableModularAccount.sol"; +import {MultiOwnerMSCAFactory} from "../../../../src/factory/MultiOwnerMSCAFactory.sol"; +import {FunctionReferenceLib} from "../../../../src/helpers/FunctionReferenceLib.sol"; import {IEntryPoint} from "../../../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../../../src/interfaces/erc4337/UserOperation.sol"; import {FunctionReference} from "../../../../src/interfaces/IPluginManager.sol"; import {Call} from "../../../../src/interfaces/IStandardExecutor.sol"; -import {FunctionReferenceLib} from "../../../../src/libraries/FunctionReferenceLib.sol"; - -import {MultiOwnerMSCAFactory} from "../../../../src/factory/MultiOwnerMSCAFactory.sol"; +import {IMultiOwnerPlugin} from "../../../../src/plugins/owner/IMultiOwnerPlugin.sol"; +import {MultiOwnerPlugin} from "../../../../src/plugins/owner/MultiOwnerPlugin.sol"; +import {ISessionKeyPlugin} from "../../../../src/plugins/session/ISessionKeyPlugin.sol"; +import {SessionKeyPlugin} from "../../../../src/plugins/session/SessionKeyPlugin.sol"; contract SessionKeyGasLimitsTest is Test { using ECDSA for bytes32; diff --git a/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol b/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol index b2dd72ab..b8e00fe8 100644 --- a/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol @@ -3,23 +3,22 @@ pragma solidity ^0.8.22; import {Test, console} from "forge-std/Test.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {UpgradeableModularAccount} from "../../../../src/account/UpgradeableModularAccount.sol"; -import {IMultiOwnerPlugin} from "../../../../src/plugins/owner/IMultiOwnerPlugin.sol"; -import {MultiOwnerPlugin} from "../../../../src/plugins/owner/MultiOwnerPlugin.sol"; -import {ISessionKeyPlugin} from "../../../../src/plugins/session/ISessionKeyPlugin.sol"; -import {SessionKeyPlugin} from "../../../../src/plugins/session/SessionKeyPlugin.sol"; import {ISessionKeyPermissionsUpdates} from "../../../../src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol"; +import {UpgradeableModularAccount} from "../../../../src/account/UpgradeableModularAccount.sol"; +import {MultiOwnerMSCAFactory} from "../../../../src/factory/MultiOwnerMSCAFactory.sol"; +import {FunctionReferenceLib} from "../../../../src/helpers/FunctionReferenceLib.sol"; import {IEntryPoint} from "../../../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../../../src/interfaces/erc4337/UserOperation.sol"; -import {Call} from "../../../../src/interfaces/IStandardExecutor.sol"; import {FunctionReference} from "../../../../src/interfaces/IPluginManager.sol"; -import {FunctionReferenceLib} from "../../../../src/libraries/FunctionReferenceLib.sol"; - -import {MultiOwnerMSCAFactory} from "../../../../src/factory/MultiOwnerMSCAFactory.sol"; +import {Call} from "../../../../src/interfaces/IStandardExecutor.sol"; +import {IMultiOwnerPlugin} from "../../../../src/plugins/owner/IMultiOwnerPlugin.sol"; +import {MultiOwnerPlugin} from "../../../../src/plugins/owner/MultiOwnerPlugin.sol"; +import {ISessionKeyPlugin} from "../../../../src/plugins/session/ISessionKeyPlugin.sol"; +import {SessionKeyPlugin} from "../../../../src/plugins/session/SessionKeyPlugin.sol"; contract SessionKeyNativeTokenSpendLimitsTest is Test { using ECDSA for bytes32; diff --git a/test/plugin/session/permissions/SessionKeyPermissions.t.sol b/test/plugin/session/permissions/SessionKeyPermissions.t.sol index 162b7f68..a8bbe8c4 100644 --- a/test/plugin/session/permissions/SessionKeyPermissions.t.sol +++ b/test/plugin/session/permissions/SessionKeyPermissions.t.sol @@ -3,24 +3,23 @@ pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {UpgradeableModularAccount} from "../../../../src/account/UpgradeableModularAccount.sol"; -import {IMultiOwnerPlugin} from "../../../../src/plugins/owner/IMultiOwnerPlugin.sol"; -import {MultiOwnerPlugin} from "../../../../src/plugins/owner/MultiOwnerPlugin.sol"; -import {ISessionKeyPlugin} from "../../../../src/plugins/session/ISessionKeyPlugin.sol"; -import {SessionKeyPlugin} from "../../../../src/plugins/session/SessionKeyPlugin.sol"; import {ISessionKeyPermissionsUpdates} from "../../../../src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol"; +import {UpgradeableModularAccount} from "../../../../src/account/UpgradeableModularAccount.sol"; +import {MultiOwnerMSCAFactory} from "../../../../src/factory/MultiOwnerMSCAFactory.sol"; +import {FunctionReferenceLib} from "../../../../src/helpers/FunctionReferenceLib.sol"; import {IEntryPoint} from "../../../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../../../src/interfaces/erc4337/UserOperation.sol"; -import {Call} from "../../../../src/interfaces/IStandardExecutor.sol"; import {FunctionReference} from "../../../../src/interfaces/IPluginManager.sol"; -import {FunctionReferenceLib} from "../../../../src/libraries/FunctionReferenceLib.sol"; - +import {Call} from "../../../../src/interfaces/IStandardExecutor.sol"; +import {IMultiOwnerPlugin} from "../../../../src/plugins/owner/IMultiOwnerPlugin.sol"; +import {MultiOwnerPlugin} from "../../../../src/plugins/owner/MultiOwnerPlugin.sol"; +import {ISessionKeyPlugin} from "../../../../src/plugins/session/ISessionKeyPlugin.sol"; +import {SessionKeyPlugin} from "../../../../src/plugins/session/SessionKeyPlugin.sol"; import {Counter} from "../../../mocks/Counter.sol"; -import {MultiOwnerMSCAFactory} from "../../../../src/factory/MultiOwnerMSCAFactory.sol"; contract SessionKeyPermissionsTest is Test { using ECDSA for bytes32; From 28b44f7e68956a432e626414453343e01d9d79ab Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Tue, 23 Jan 2024 00:16:55 -0500 Subject: [PATCH 091/106] refactor: move test files to appropriate folders (#104) --- test/{libraries => account}/AccountStorage.t.sol | 0 test/{libraries => helpers}/FunctionReferenceLib.t.sol | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename test/{libraries => account}/AccountStorage.t.sol (100%) rename test/{libraries => helpers}/FunctionReferenceLib.t.sol (100%) diff --git a/test/libraries/AccountStorage.t.sol b/test/account/AccountStorage.t.sol similarity index 100% rename from test/libraries/AccountStorage.t.sol rename to test/account/AccountStorage.t.sol diff --git a/test/libraries/FunctionReferenceLib.t.sol b/test/helpers/FunctionReferenceLib.t.sol similarity index 100% rename from test/libraries/FunctionReferenceLib.t.sol rename to test/helpers/FunctionReferenceLib.t.sol From 52a55775d4675d6982679706c0246f762905dfa5 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Tue, 23 Jan 2024 00:32:18 -0500 Subject: [PATCH 092/106] refactor: sort imports (#105) --- src/account/AccountStorageV1.sol | 1 - src/factory/MultiOwnerMSCAFactory.sol | 8 ++++---- src/factory/MultiOwnerTokenReceiverMSCAFactory.sol | 8 ++++---- src/helpers/KnownSelectors.sol | 10 +++++----- src/libraries/CountableLinkedListSetLib.sol | 2 +- src/plugins/BasePlugin.sol | 2 +- src/plugins/TokenReceiverPlugin.sol | 4 ++-- src/plugins/session/ISessionKeyPlugin.sol | 2 +- .../session/permissions/SessionKeyPermissions.sol | 7 +++---- .../session/permissions/SessionKeyPermissionsBase.sol | 3 +-- test/account/AccountReturnData.t.sol | 5 ++--- test/account/ExecuteFromPluginPermissions.t.sol | 9 +++------ test/account/ManifestValidity.t.sol | 5 ++--- test/account/UpgradeableModularAccount.t.sol | 11 +++++------ test/account/ValidationIntersection.t.sol | 3 +-- test/account/phases/AccountStatePhasesExec.t.sol | 8 +++----- .../phases/AccountStatePhasesRTValidation.t.sol | 8 +++----- test/comparison/CompareSimpleAccount.t.sol | 2 +- test/factory/MultiOwnerMSCAFactoryTest.t.sol | 2 +- test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol | 8 ++++---- test/helpers/KnownSelectors.t.sol | 6 +++--- test/invariant/LLSLRepro.t.sol | 2 +- .../handlers/AssociatedLinkedListSetHandler.sol | 3 ++- test/invariant/handlers/LinkedListSetHandler.sol | 5 +++-- test/libraries/CountableLinkedListSetLib.t.sol | 2 +- test/libraries/LinkedListSetLib.t.sol | 2 +- test/mocks/ContractOwner.sol | 3 +-- test/mocks/Counter.t.sol | 1 + test/mocks/plugins/AccountStateMutatingPlugin.sol | 3 +-- test/mocks/plugins/BadTransferOwnershipPlugin.sol | 6 +++--- test/mocks/plugins/BaseTestPlugin.sol | 2 +- test/mocks/plugins/ExecFromPluginPermissionsMocks.sol | 7 +++---- test/mocks/plugins/ManifestValidityMocks.sol | 4 ++-- test/mocks/plugins/ReturnDataPluginMocks.sol | 4 ++-- test/plugin/TokenReceiverPlugin.t.sol | 11 +++++------ test/plugin/owner/MultiOwnerPlugin.t.sol | 7 +++---- test/plugin/owner/MultiOwnerPluginIntegration.t.sol | 11 ++++------- test/upgrade/LightAccountToMSCA.t.sol | 3 +-- test/upgrade/MSCAToMSCA.t.sol | 7 +++---- 39 files changed, 88 insertions(+), 109 deletions(-) diff --git a/src/account/AccountStorageV1.sol b/src/account/AccountStorageV1.sol index 0cd21303..ceefd78c 100644 --- a/src/account/AccountStorageV1.sol +++ b/src/account/AccountStorageV1.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.22; import {IPlugin} from "../interfaces/IPlugin.sol"; import {FunctionReference} from "../interfaces/IPluginManager.sol"; - import {LinkedListSet} from "../libraries/LinkedListSetLib.sol"; /// @title Account Storage V1 diff --git a/src/factory/MultiOwnerMSCAFactory.sol b/src/factory/MultiOwnerMSCAFactory.sol index 4fb7a27c..47a76915 100644 --- a/src/factory/MultiOwnerMSCAFactory.sol +++ b/src/factory/MultiOwnerMSCAFactory.sol @@ -1,15 +1,15 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.22; -import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; +import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol"; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; -import {IAccountInitializable} from "../interfaces/IAccountInitializable.sol"; -import {IEntryPoint} from "../interfaces/erc4337/IEntryPoint.sol"; import {FactoryHelpers} from "../helpers/FactoryHelpers.sol"; +import {IEntryPoint} from "../interfaces/erc4337/IEntryPoint.sol"; +import {IAccountInitializable} from "../interfaces/IAccountInitializable.sol"; /// @title Multi Owner Plugin MSCA (Modular Smart Contract Account) Factory /// @author Alchemy diff --git a/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol b/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol index 14df1ea2..d204bd1a 100644 --- a/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol +++ b/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol @@ -1,15 +1,15 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.22; -import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; +import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol"; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; -import {IAccountInitializable} from "../interfaces/IAccountInitializable.sol"; -import {IEntryPoint} from "../interfaces/erc4337/IEntryPoint.sol"; import {FactoryHelpers} from "../helpers/FactoryHelpers.sol"; +import {IEntryPoint} from "../interfaces/erc4337/IEntryPoint.sol"; +import {IAccountInitializable} from "../interfaces/IAccountInitializable.sol"; /// @title Multi Owner Plugin + Token Receiver MSCA (Modular Smart Contract Account) Factory /// @author Alchemy diff --git a/src/helpers/KnownSelectors.sol b/src/helpers/KnownSelectors.sol index 2298f5e3..6c5efc39 100644 --- a/src/helpers/KnownSelectors.sol +++ b/src/helpers/KnownSelectors.sol @@ -1,18 +1,18 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.22; -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {UUPSUpgradeable} from "../../ext/UUPSUpgradeable.sol"; +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {IAccount} from "../../src/interfaces/erc4337/IAccount.sol"; -import {IAccountInitializable} from "../interfaces/IAccountInitializable.sol"; -import {IAccountLoupe} from "../../src/interfaces/IAccountLoupe.sol"; -import {IAccountView} from "../../src/interfaces/IAccountView.sol"; import {IAggregator} from "../../src/interfaces/erc4337/IAggregator.sol"; import {IPaymaster} from "../../src/interfaces/erc4337/IPaymaster.sol"; +import {IAccountLoupe} from "../../src/interfaces/IAccountLoupe.sol"; +import {IAccountView} from "../../src/interfaces/IAccountView.sol"; +import {IPluginManager} from "../../src/interfaces/IPluginManager.sol"; +import {IAccountInitializable} from "../interfaces/IAccountInitializable.sol"; import {IPlugin} from "../interfaces/IPlugin.sol"; import {IPluginExecutor} from "../interfaces/IPluginExecutor.sol"; -import {IPluginManager} from "../../src/interfaces/IPluginManager.sol"; import {IStandardExecutor} from "../interfaces/IStandardExecutor.sol"; /// @title Known Selectors diff --git a/src/libraries/CountableLinkedListSetLib.sol b/src/libraries/CountableLinkedListSetLib.sol index b04c7ff0..8b84da91 100644 --- a/src/libraries/CountableLinkedListSetLib.sol +++ b/src/libraries/CountableLinkedListSetLib.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.22; -import {LinkedListSet, LinkedListSetLib} from "./LinkedListSetLib.sol"; import {SetValue} from "./Constants.sol"; +import {LinkedListSet, LinkedListSetLib} from "./LinkedListSetLib.sol"; /// @title Countable Linked List Set Library /// @author Alchemy diff --git a/src/plugins/BasePlugin.sol b/src/plugins/BasePlugin.sol index d02900cb..e873e596 100644 --- a/src/plugins/BasePlugin.sol +++ b/src/plugins/BasePlugin.sol @@ -3,8 +3,8 @@ pragma solidity ^0.8.22; import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; -import {IPlugin, PluginManifest, PluginMetadata} from "../interfaces/IPlugin.sol"; import {UserOperation} from "../interfaces/erc4337/UserOperation.sol"; +import {IPlugin, PluginManifest, PluginMetadata} from "../interfaces/IPlugin.sol"; /// @title Base contract for plugins /// @dev Implements ERC-165 to support IPlugin's interface, which is a requirement diff --git a/src/plugins/TokenReceiverPlugin.sol b/src/plugins/TokenReceiverPlugin.sol index cb8c0606..a1e3b033 100644 --- a/src/plugins/TokenReceiverPlugin.sol +++ b/src/plugins/TokenReceiverPlugin.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.22; -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 {IERC777Recipient} from "@openzeppelin/contracts/interfaces/IERC777Recipient.sol"; +import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import { ManifestFunction, diff --git a/src/plugins/session/ISessionKeyPlugin.sol b/src/plugins/session/ISessionKeyPlugin.sol index 4041350a..bb7cd040 100644 --- a/src/plugins/session/ISessionKeyPlugin.sol +++ b/src/plugins/session/ISessionKeyPlugin.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.22; -import {Call} from "../../interfaces/IStandardExecutor.sol"; import {UserOperation} from "../../interfaces/erc4337/UserOperation.sol"; +import {Call} from "../../interfaces/IStandardExecutor.sol"; interface ISessionKeyPlugin { enum FunctionId { diff --git a/src/plugins/session/permissions/SessionKeyPermissions.sol b/src/plugins/session/permissions/SessionKeyPermissions.sol index bbce8120..dbfa56a7 100644 --- a/src/plugins/session/permissions/SessionKeyPermissions.sol +++ b/src/plugins/session/permissions/SessionKeyPermissions.sol @@ -3,15 +3,14 @@ pragma solidity ^0.8.22; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {UserOperation} from "../../../interfaces/erc4337/UserOperation.sol"; import {Call} from "../../../interfaces/IStandardExecutor.sol"; +import {IStandardExecutor} from "../../../interfaces/IStandardExecutor.sol"; +import {SIG_VALIDATION_PASSED, SIG_VALIDATION_FAILED} from "../../../libraries/Constants.sol"; import {ISessionKeyPlugin} from "../ISessionKeyPlugin.sol"; import {ISessionKeyPermissionsUpdates} from "./ISessionKeyPermissionsUpdates.sol"; import {SessionKeyPermissionsLoupe} from "./SessionKeyPermissionsLoupe.sol"; -import {IStandardExecutor} from "../../../interfaces/IStandardExecutor.sol"; -import {UserOperation} from "../../../interfaces/erc4337/UserOperation.sol"; -import {SIG_VALIDATION_PASSED, SIG_VALIDATION_FAILED} from "../../../libraries/Constants.sol"; - /// @title Session Key Permissions /// @author Alchemy /// @notice This plugin allows users to configure and enforce permissions on session keys that have been diff --git a/src/plugins/session/permissions/SessionKeyPermissionsBase.sol b/src/plugins/session/permissions/SessionKeyPermissionsBase.sol index bba2d288..5519944b 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsBase.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsBase.sol @@ -1,9 +1,8 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.22; -import {ISessionKeyPlugin} from "../ISessionKeyPlugin.sol"; - import {PluginStorageLib, StoragePointer} from "../../../libraries/PluginStorageLib.sol"; +import {ISessionKeyPlugin} from "../ISessionKeyPlugin.sol"; abstract contract SessionKeyPermissionsBase is ISessionKeyPlugin { type SessionKeyId is bytes32; diff --git a/test/account/AccountReturnData.t.sol b/test/account/AccountReturnData.t.sol index a4d67c06..4e27426c 100644 --- a/test/account/AccountReturnData.t.sol +++ b/test/account/AccountReturnData.t.sol @@ -6,17 +6,16 @@ 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 {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; import {FunctionReference} from "../../src/interfaces/IPluginManager.sol"; -import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; import {Call} from "../../src/interfaces/IStandardExecutor.sol"; - +import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; import { RegularResultContract, ResultCreatorPlugin, ResultConsumerPlugin } from "../mocks/plugins/ReturnDataPluginMocks.sol"; -import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; // Tests all the different ways that return data can be read from plugins through an account contract AccountReturnDataTest is Test { diff --git a/test/account/ExecuteFromPluginPermissions.t.sol b/test/account/ExecuteFromPluginPermissions.t.sol index c6a52b0a..f540e8b9 100644 --- a/test/account/ExecuteFromPluginPermissions.t.sol +++ b/test/account/ExecuteFromPluginPermissions.t.sol @@ -5,23 +5,20 @@ import {Test, console} from "forge-std/Test.sol"; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; -import {IPlugin} from "../../src/interfaces/IPlugin.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; +import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; +import {IPlugin} from "../../src/interfaces/IPlugin.sol"; import {FunctionReference} from "../../src/interfaces/IPluginManager.sol"; import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; - -import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; - import {Counter} from "../mocks/Counter.sol"; -import {ResultCreatorPlugin} from "../mocks/plugins/ReturnDataPluginMocks.sol"; - import { EFPCallerPlugin, EFPCallerPluginAnyExternal, EFPCallerPluginAnyExternalCanSpendNativeToken, EFPExecutionHookPlugin } from "../mocks/plugins/ExecFromPluginPermissionsMocks.sol"; +import {ResultCreatorPlugin} from "../mocks/plugins/ReturnDataPluginMocks.sol"; contract ExecuteFromPluginPermissionsTest is Test { Counter public counter1; diff --git a/test/account/ManifestValidity.t.sol b/test/account/ManifestValidity.t.sol index a547ab95..116a4022 100644 --- a/test/account/ManifestValidity.t.sol +++ b/test/account/ManifestValidity.t.sol @@ -5,13 +5,12 @@ 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 {PluginManagerInternals} from "../../src/account/PluginManagerInternals.sol"; +import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; +import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; import {FunctionReference} from "../../src/interfaces/IPluginManager.sol"; import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; - -import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; import { BadValidationMagicValue_UserOp_Plugin, BadValidationMagicValue_PreRuntimeValidationHook_Plugin, diff --git a/test/account/UpgradeableModularAccount.t.sol b/test/account/UpgradeableModularAccount.t.sol index e6debda8..f28729a6 100644 --- a/test/account/UpgradeableModularAccount.t.sol +++ b/test/account/UpgradeableModularAccount.t.sol @@ -10,19 +10,18 @@ import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.so import {AccountExecutor} from "../../src/account/AccountExecutor.sol"; import {PluginManagerInternals} from "../../src/account/PluginManagerInternals.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; -import {IMultiOwnerPlugin} from "../../src/plugins/owner/IMultiOwnerPlugin.sol"; -import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; -import {SessionKeyPlugin} from "../../src/plugins/session/SessionKeyPlugin.sol"; -import {TokenReceiverPlugin} from "../../src/plugins/TokenReceiverPlugin.sol"; +import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../src/interfaces/erc4337/UserOperation.sol"; import {IAccountInitializable} from "../../src/interfaces/IAccountInitializable.sol"; import {IPlugin, PluginManifest} from "../../src/interfaces/IPlugin.sol"; import {FunctionReference, IPluginManager} from "../../src/interfaces/IPluginManager.sol"; import {Call} from "../../src/interfaces/IStandardExecutor.sol"; - +import {IMultiOwnerPlugin} from "../../src/plugins/owner/IMultiOwnerPlugin.sol"; +import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; +import {SessionKeyPlugin} from "../../src/plugins/session/SessionKeyPlugin.sol"; +import {TokenReceiverPlugin} from "../../src/plugins/TokenReceiverPlugin.sol"; import {Counter} from "../mocks/Counter.sol"; -import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; import {MockPlugin} from "../mocks/MockPlugin.sol"; contract UpgradeableModularAccountTest is Test { diff --git a/test/account/ValidationIntersection.t.sol b/test/account/ValidationIntersection.t.sol index ced8e029..7c3214fb 100644 --- a/test/account/ValidationIntersection.t.sol +++ b/test/account/ValidationIntersection.t.sol @@ -6,13 +6,12 @@ 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 {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../src/interfaces/erc4337/UserOperation.sol"; import {FunctionReference} from "../../src/interfaces/IPluginManager.sol"; import {SIG_VALIDATION_FAILED, SIG_VALIDATION_PASSED} from "../../src/libraries/Constants.sol"; import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; - -import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; import { MockBaseUserOpValidationPlugin, MockUserOpValidation1HookPlugin, diff --git a/test/account/phases/AccountStatePhasesExec.t.sol b/test/account/phases/AccountStatePhasesExec.t.sol index 4107cba5..07551e78 100644 --- a/test/account/phases/AccountStatePhasesExec.t.sol +++ b/test/account/phases/AccountStatePhasesExec.t.sol @@ -1,9 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.22; -import {AccountStatePhasesTest} from "./AccountStatePhases.t.sol"; - -import {IPluginManager} from "../../../src/interfaces/IPluginManager.sol"; import { IPlugin, PluginManifest, @@ -12,10 +9,11 @@ import { ManifestAssociatedFunctionType, ManifestFunction } from "../../../src/interfaces/IPlugin.sol"; +import {IPluginManager} from "../../../src/interfaces/IPluginManager.sol"; import {IStandardExecutor, Call} from "../../../src/interfaces/IStandardExecutor.sol"; - -import {AccountStateMutatingPlugin} from "../../mocks/plugins/AccountStateMutatingPlugin.sol"; import {MockPlugin} from "../../mocks/MockPlugin.sol"; +import {AccountStateMutatingPlugin} from "../../mocks/plugins/AccountStateMutatingPlugin.sol"; +import {AccountStatePhasesTest} from "./AccountStatePhases.t.sol"; // Tests the account state phase behavior when the source of the state modification happens during execution. contract AccountStatePhasesUOValidationTest is AccountStatePhasesTest { diff --git a/test/account/phases/AccountStatePhasesRTValidation.t.sol b/test/account/phases/AccountStatePhasesRTValidation.t.sol index 809e2ed6..f572baaf 100644 --- a/test/account/phases/AccountStatePhasesRTValidation.t.sol +++ b/test/account/phases/AccountStatePhasesRTValidation.t.sol @@ -1,14 +1,12 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.22; -import {AccountStatePhasesTest} from "./AccountStatePhases.t.sol"; - -import {IPluginManager} from "../../../src/interfaces/IPluginManager.sol"; +import {UpgradeableModularAccount} from "../../../src/account/UpgradeableModularAccount.sol"; import {IPlugin} from "../../../src/interfaces/IPlugin.sol"; +import {IPluginManager} from "../../../src/interfaces/IPluginManager.sol"; import {IStandardExecutor, Call} from "../../../src/interfaces/IStandardExecutor.sol"; -import {UpgradeableModularAccount} from "../../../src/account/UpgradeableModularAccount.sol"; - import {AccountStateMutatingPlugin} from "../../mocks/plugins/AccountStateMutatingPlugin.sol"; +import {AccountStatePhasesTest} from "./AccountStatePhases.t.sol"; // Tests the account state phase behavior when the source of the state modification // happens during runtime validation. diff --git a/test/comparison/CompareSimpleAccount.t.sol b/test/comparison/CompareSimpleAccount.t.sol index 3b742fb2..e48c2d3e 100644 --- a/test/comparison/CompareSimpleAccount.t.sol +++ b/test/comparison/CompareSimpleAccount.t.sol @@ -3,10 +3,10 @@ pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; import {SimpleAccount} from "@eth-infinitism/account-abstraction/samples/SimpleAccount.sol"; import {SimpleAccountFactory} from "@eth-infinitism/account-abstraction/samples/SimpleAccountFactory.sol"; +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../src/interfaces/erc4337/UserOperation.sol"; diff --git a/test/factory/MultiOwnerMSCAFactoryTest.t.sol b/test/factory/MultiOwnerMSCAFactoryTest.t.sol index 479594a7..07628e3f 100644 --- a/test/factory/MultiOwnerMSCAFactoryTest.t.sol +++ b/test/factory/MultiOwnerMSCAFactoryTest.t.sol @@ -6,10 +6,10 @@ import {Test} from "forge-std/Test.sol"; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; -import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; contract MultiOwnerMSCAFactoryTest is Test { using ECDSA for bytes32; diff --git a/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol b/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol index eebae3d5..d771ca33 100644 --- a/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol +++ b/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol @@ -3,18 +3,18 @@ pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; -import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {ERC721PresetMinterPauserAutoId} from "@openzeppelin/contracts/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol"; +import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {MultiOwnerTokenReceiverMSCAFactory} from "../../src/factory/MultiOwnerTokenReceiverMSCAFactory.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; import {TokenReceiverPlugin} from "../../src/plugins/TokenReceiverPlugin.sol"; -import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; -import {MockERC777} from "../mocks/tokens/MockERC777.sol"; import {MockERC1155} from "../mocks/tokens/MockERC1155.sol"; +import {MockERC777} from "../mocks/tokens/MockERC777.sol"; contract MultiOwnerTokenReceiverMSCAFactoryTest is Test { using ECDSA for bytes32; diff --git a/test/helpers/KnownSelectors.t.sol b/test/helpers/KnownSelectors.t.sol index 864f9113..978e3dbd 100644 --- a/test/helpers/KnownSelectors.t.sol +++ b/test/helpers/KnownSelectors.t.sol @@ -3,15 +3,15 @@ pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; import {BaseAccount} from "@eth-infinitism/account-abstraction/core/BaseAccount.sol"; import {IAggregator} from "@eth-infinitism/account-abstraction/interfaces/IAggregator.sol"; import {IPaymaster} from "@eth-infinitism/account-abstraction/interfaces/IPaymaster.sol"; +import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {KnownSelectors} from "../../src/helpers/KnownSelectors.sol"; -import {IAccountLoupe} from "../../src/interfaces/IAccountLoupe.sol"; import {IAccountInitializable} from "../../src/interfaces/IAccountInitializable.sol"; +import {IAccountLoupe} from "../../src/interfaces/IAccountLoupe.sol"; import {IPlugin} from "../../src/interfaces/IPlugin.sol"; import {IPluginExecutor} from "../../src/interfaces/IPluginExecutor.sol"; import {IPluginManager} from "../../src/interfaces/IPluginManager.sol"; diff --git a/test/invariant/LLSLRepro.t.sol b/test/invariant/LLSLRepro.t.sol index 9a3f4307..6d9984c5 100644 --- a/test/invariant/LLSLRepro.t.sol +++ b/test/invariant/LLSLRepro.t.sol @@ -3,8 +3,8 @@ pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; -import {LinkedListSetHandler} from "./handlers/LinkedListSetHandler.sol"; import {AssociatedLinkedListSetHandler} from "./handlers/AssociatedLinkedListSetHandler.sol"; +import {LinkedListSetHandler} from "./handlers/LinkedListSetHandler.sol"; contract LLSLReproTest is Test { LinkedListSetHandler public handler; diff --git a/test/invariant/handlers/AssociatedLinkedListSetHandler.sol b/test/invariant/handlers/AssociatedLinkedListSetHandler.sol index d0c387d3..ffbec216 100644 --- a/test/invariant/handlers/AssociatedLinkedListSetHandler.sol +++ b/test/invariant/handlers/AssociatedLinkedListSetHandler.sol @@ -5,8 +5,9 @@ import {CommonBase} from "forge-std/Base.sol"; import {StdCheats} from "forge-std/StdCheats.sol"; import {StdUtils} from "forge-std/StdUtils.sol"; -import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; +import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; + import { AssociatedLinkedListSet, AssociatedLinkedListSetLib diff --git a/test/invariant/handlers/LinkedListSetHandler.sol b/test/invariant/handlers/LinkedListSetHandler.sol index 1468ce5f..a3d59bc4 100644 --- a/test/invariant/handlers/LinkedListSetHandler.sol +++ b/test/invariant/handlers/LinkedListSetHandler.sol @@ -5,10 +5,11 @@ import {CommonBase} from "forge-std/Base.sol"; import {StdCheats} from "forge-std/StdCheats.sol"; import {StdUtils} from "forge-std/StdUtils.sol"; -import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; -import {LinkedListSetLib, LinkedListSet as EnumerableSetType} from "../../../src/libraries/LinkedListSetLib.sol"; +import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; + import {SetValue} from "../../../src/libraries/Constants.sol"; +import {LinkedListSetLib, LinkedListSet as EnumerableSetType} from "../../../src/libraries/LinkedListSetLib.sol"; /// @notice A handler contract for differential invariant testing LinkedListSetLib /// This contract maps logic for adding, removeing, clearing, and inspecting a list diff --git a/test/libraries/CountableLinkedListSetLib.t.sol b/test/libraries/CountableLinkedListSetLib.t.sol index b971de1a..0c57faae 100644 --- a/test/libraries/CountableLinkedListSetLib.t.sol +++ b/test/libraries/CountableLinkedListSetLib.t.sol @@ -3,9 +3,9 @@ pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; +import {SetValue} from "../../src/libraries/Constants.sol"; import {CountableLinkedListSetLib} from "../../src/libraries/CountableLinkedListSetLib.sol"; import {LinkedListSet, LinkedListSetLib} from "../../src/libraries/LinkedListSetLib.sol"; -import {SetValue} from "../../src/libraries/Constants.sol"; contract CountableLinkedListSetLibTest is Test { using LinkedListSetLib for LinkedListSet; diff --git a/test/libraries/LinkedListSetLib.t.sol b/test/libraries/LinkedListSetLib.t.sol index 4373cc52..73a39b95 100644 --- a/test/libraries/LinkedListSetLib.t.sol +++ b/test/libraries/LinkedListSetLib.t.sol @@ -3,8 +3,8 @@ pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; -import {LinkedListSet, LinkedListSetLib} from "../../src/libraries/LinkedListSetLib.sol"; import {SetValue, SENTINEL_VALUE} from "../../src/libraries/Constants.sol"; +import {LinkedListSet, LinkedListSetLib} from "../../src/libraries/LinkedListSetLib.sol"; // Ported over from test/AssociatedLinkedListSetLib.t.sol, dropping test_no_address_collision contract LinkedListSetLibTest is Test { diff --git a/test/mocks/ContractOwner.sol b/test/mocks/ContractOwner.sol index e96f8044..35704e19 100644 --- a/test/mocks/ContractOwner.sol +++ b/test/mocks/ContractOwner.sol @@ -1,9 +1,8 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.22; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; - import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol"; +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; contract ContractOwner is IERC1271 { bytes4 internal constant _1271_MAGIC_VALUE = 0x1626ba7e; diff --git a/test/mocks/Counter.t.sol b/test/mocks/Counter.t.sol index 01b3d793..70f98df7 100644 --- a/test/mocks/Counter.t.sol +++ b/test/mocks/Counter.t.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; + import {Counter} from "./Counter.sol"; contract CounterTest is Test { diff --git a/test/mocks/plugins/AccountStateMutatingPlugin.sol b/test/mocks/plugins/AccountStateMutatingPlugin.sol index 5c246638..1480d91d 100644 --- a/test/mocks/plugins/AccountStateMutatingPlugin.sol +++ b/test/mocks/plugins/AccountStateMutatingPlugin.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.22; +import {UserOperation} from "../../../src/interfaces/erc4337/UserOperation.sol"; import { PluginManifest, ManifestExecutionHook, @@ -8,8 +9,6 @@ import { ManifestAssociatedFunctionType, ManifestAssociatedFunction } from "../../../src/interfaces/IPlugin.sol"; -import {UserOperation} from "../../../src/interfaces/erc4337/UserOperation.sol"; - import {BaseTestPlugin} from "./BaseTestPlugin.sol"; // Used in conjunction with AccountStatePhasesTest to verify that the account state is consistent when plugins are diff --git a/test/mocks/plugins/BadTransferOwnershipPlugin.sol b/test/mocks/plugins/BadTransferOwnershipPlugin.sol index 9403974b..27a5e07f 100644 --- a/test/mocks/plugins/BadTransferOwnershipPlugin.sol +++ b/test/mocks/plugins/BadTransferOwnershipPlugin.sol @@ -9,10 +9,10 @@ import { PluginManifest, PluginMetadata } from "../../../src/interfaces/IPlugin.sol"; -import {BaseTestPlugin} from "./BaseTestPlugin.sol"; -import {IMultiOwnerPlugin} from "../../../src/plugins/owner/IMultiOwnerPlugin.sol"; -import {IStandardExecutor} from "../../../src/interfaces/IStandardExecutor.sol"; import {IPluginExecutor} from "../../../src/interfaces/IPluginExecutor.sol"; +import {IStandardExecutor} from "../../../src/interfaces/IStandardExecutor.sol"; +import {IMultiOwnerPlugin} from "../../../src/plugins/owner/IMultiOwnerPlugin.sol"; +import {BaseTestPlugin} from "./BaseTestPlugin.sol"; contract BadTransferOwnershipPlugin is BaseTestPlugin { string internal constant _NAME = "Evil Transfer Ownership Plugin"; diff --git a/test/mocks/plugins/BaseTestPlugin.sol b/test/mocks/plugins/BaseTestPlugin.sol index bbc388b0..cb28d43d 100644 --- a/test/mocks/plugins/BaseTestPlugin.sol +++ b/test/mocks/plugins/BaseTestPlugin.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.22; -import {BasePlugin} from "../../../src/plugins/BasePlugin.sol"; import {PluginMetadata} from "../../../src/interfaces/IPlugin.sol"; +import {BasePlugin} from "../../../src/plugins/BasePlugin.sol"; contract BaseTestPlugin is BasePlugin { // Don't need to implement this in each context diff --git a/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol b/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol index 23c581be..3db37e36 100644 --- a/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol +++ b/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol @@ -9,14 +9,13 @@ import { ManifestExecutionHook, PluginManifest } from "../../../src/interfaces/IPlugin.sol"; -import {IStandardExecutor} from "../../../src/interfaces/IStandardExecutor.sol"; -import {IPluginExecutor} from "../../../src/interfaces/IPluginExecutor.sol"; import {IPlugin} from "../../../src/interfaces/IPlugin.sol"; +import {IPluginExecutor} from "../../../src/interfaces/IPluginExecutor.sol"; import {FunctionReference} from "../../../src/interfaces/IPluginManager.sol"; +import {IStandardExecutor} from "../../../src/interfaces/IStandardExecutor.sol"; +import {Counter} from "../Counter.sol"; import {BaseTestPlugin} from "./BaseTestPlugin.sol"; - import {ResultCreatorPlugin} from "./ReturnDataPluginMocks.sol"; -import {Counter} from "../Counter.sol"; // Hardcode the counter addresses from ExecuteFromPluginPermissionsTest to be able to have a pure plugin manifest // easily diff --git a/test/mocks/plugins/ManifestValidityMocks.sol b/test/mocks/plugins/ManifestValidityMocks.sol index 33b5d67f..1cb17ee3 100644 --- a/test/mocks/plugins/ManifestValidityMocks.sol +++ b/test/mocks/plugins/ManifestValidityMocks.sol @@ -9,10 +9,10 @@ import { ManifestExternalCallPermission, PluginManifest } from "../../../src/interfaces/IPlugin.sol"; -import {IStandardExecutor} from "../../../src/interfaces/IStandardExecutor.sol"; -import {IPluginExecutor} from "../../../src/interfaces/IPluginExecutor.sol"; import {IPlugin} from "../../../src/interfaces/IPlugin.sol"; +import {IPluginExecutor} from "../../../src/interfaces/IPluginExecutor.sol"; import {FunctionReference} from "../../../src/interfaces/IPluginManager.sol"; +import {IStandardExecutor} from "../../../src/interfaces/IStandardExecutor.sol"; import {BaseTestPlugin} from "./BaseTestPlugin.sol"; contract BadValidationMagicValue_UserOp_Plugin is BaseTestPlugin { diff --git a/test/mocks/plugins/ReturnDataPluginMocks.sol b/test/mocks/plugins/ReturnDataPluginMocks.sol index 1c695a2e..a1cc4da7 100644 --- a/test/mocks/plugins/ReturnDataPluginMocks.sol +++ b/test/mocks/plugins/ReturnDataPluginMocks.sol @@ -8,10 +8,10 @@ import { ManifestExternalCallPermission, PluginManifest } from "../../../src/interfaces/IPlugin.sol"; -import {IStandardExecutor} from "../../../src/interfaces/IStandardExecutor.sol"; -import {IPluginExecutor} from "../../../src/interfaces/IPluginExecutor.sol"; import {IPlugin} from "../../../src/interfaces/IPlugin.sol"; +import {IPluginExecutor} from "../../../src/interfaces/IPluginExecutor.sol"; import {FunctionReference} from "../../../src/interfaces/IPluginManager.sol"; +import {IStandardExecutor} from "../../../src/interfaces/IStandardExecutor.sol"; import {BaseTestPlugin} from "./BaseTestPlugin.sol"; contract RegularResultContract { diff --git a/test/plugin/TokenReceiverPlugin.t.sol b/test/plugin/TokenReceiverPlugin.t.sol index c9b1e656..4fa9ecfc 100644 --- a/test/plugin/TokenReceiverPlugin.t.sol +++ b/test/plugin/TokenReceiverPlugin.t.sol @@ -3,23 +3,22 @@ pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; -import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.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 {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; +import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; +import {IERC777Recipient} from "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol"; import {AccountStorageV1} from "../../src/account/AccountStorageV1.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; +import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; import {IEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; import {FunctionReference} from "../../src/interfaces/IPluginManager.sol"; import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; import {TokenReceiverPlugin} from "../../src/plugins/TokenReceiverPlugin.sol"; - -import {MockERC777} from "../mocks/tokens/MockERC777.sol"; import {MockERC1155} from "../mocks/tokens/MockERC1155.sol"; -import {MultiOwnerMSCAFactory} from "../../src/factory/MultiOwnerMSCAFactory.sol"; +import {MockERC777} from "../mocks/tokens/MockERC777.sol"; contract TokenReceiverPluginTest is Test, IERC1155Receiver, AccountStorageV1 { UpgradeableModularAccount public acct; diff --git a/test/plugin/owner/MultiOwnerPlugin.t.sol b/test/plugin/owner/MultiOwnerPlugin.t.sol index 3d68ce1d..ca784690 100644 --- a/test/plugin/owner/MultiOwnerPlugin.t.sol +++ b/test/plugin/owner/MultiOwnerPlugin.t.sol @@ -6,13 +6,12 @@ import {Test} from "forge-std/Test.sol"; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {MultiOwnerPlugin} from "../../../src/plugins/owner/MultiOwnerPlugin.sol"; -import {IMultiOwnerPlugin} from "../../../src/plugins/owner/IMultiOwnerPlugin.sol"; -import {BasePlugin} from "../../../src/plugins/BasePlugin.sol"; import {IEntryPoint} from "../../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../../src/interfaces/erc4337/UserOperation.sol"; import {PluginManifest} from "../../../src/interfaces/IPlugin.sol"; - +import {BasePlugin} from "../../../src/plugins/BasePlugin.sol"; +import {IMultiOwnerPlugin} from "../../../src/plugins/owner/IMultiOwnerPlugin.sol"; +import {MultiOwnerPlugin} from "../../../src/plugins/owner/MultiOwnerPlugin.sol"; import {ContractOwner} from "../../mocks/ContractOwner.sol"; import {Utils} from "../../Utils.sol"; diff --git a/test/plugin/owner/MultiOwnerPluginIntegration.t.sol b/test/plugin/owner/MultiOwnerPluginIntegration.t.sol index b086f181..23853523 100644 --- a/test/plugin/owner/MultiOwnerPluginIntegration.t.sol +++ b/test/plugin/owner/MultiOwnerPluginIntegration.t.sol @@ -4,21 +4,18 @@ pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; - -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol"; +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {UpgradeableModularAccount} from "../../../src/account/UpgradeableModularAccount.sol"; +import {MultiOwnerMSCAFactory} from "../../../src/factory/MultiOwnerMSCAFactory.sol"; import {IEntryPoint} from "../../../src/interfaces/erc4337/IEntryPoint.sol"; import {UserOperation} from "../../../src/interfaces/erc4337/UserOperation.sol"; import {FunctionReference} from "../../../src/interfaces/IPluginManager.sol"; -import {MultiOwnerPlugin} from "../../../src/plugins/owner/MultiOwnerPlugin.sol"; -import {IMultiOwnerPlugin} from "../../../src/plugins/owner/IMultiOwnerPlugin.sol"; -import {Utils} from "../../Utils.sol"; import {Call} from "../../../src/interfaces/IStandardExecutor.sol"; - +import {IMultiOwnerPlugin} from "../../../src/plugins/owner/IMultiOwnerPlugin.sol"; +import {MultiOwnerPlugin} from "../../../src/plugins/owner/MultiOwnerPlugin.sol"; import {Counter} from "../../mocks/Counter.sol"; -import {MultiOwnerMSCAFactory} from "../../../src/factory/MultiOwnerMSCAFactory.sol"; import {Utils} from "../../Utils.sol"; contract MultiOwnerPluginIntegration is Test { diff --git a/test/upgrade/LightAccountToMSCA.t.sol b/test/upgrade/LightAccountToMSCA.t.sol index 4476fa8e..2fa335cc 100644 --- a/test/upgrade/LightAccountToMSCA.t.sol +++ b/test/upgrade/LightAccountToMSCA.t.sol @@ -9,9 +9,8 @@ import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.so import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; -import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; import {IEntryPoint as IMSCAEntryPoint} from "../../src/interfaces/erc4337/IEntryPoint.sol"; - +import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; import {MockERC20} from "../mocks/tokens/MockERC20.sol"; contract LightAccountToMSCATest is Test { diff --git a/test/upgrade/MSCAToMSCA.t.sol b/test/upgrade/MSCAToMSCA.t.sol index cf365a0e..222a1294 100644 --- a/test/upgrade/MSCAToMSCA.t.sol +++ b/test/upgrade/MSCAToMSCA.t.sol @@ -7,13 +7,12 @@ import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.so 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 {Call} from "../../src/interfaces/IStandardExecutor.sol"; - -import {Utils} from "../Utils.sol"; +import {MultiOwnerPlugin} from "../../src/plugins/owner/MultiOwnerPlugin.sol"; +import {TokenReceiverPlugin} from "../../src/plugins/TokenReceiverPlugin.sol"; import {MockERC20} from "../mocks/tokens/MockERC20.sol"; +import {Utils} from "../Utils.sol"; contract MSCAToMSCATest is Test { IEntryPoint public entryPoint; From 266ea6fd13932c6332cc725ff663d4dd57022255 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Tue, 23 Jan 2024 00:36:43 -0500 Subject: [PATCH 093/106] chore: add license for libraries (#106) --- README.md | 6 ++++++ src/libraries/AssociatedLinkedListSetLib.sol | 9 ++++++++- src/libraries/Constants.sol | 9 ++++++++- src/libraries/CountableLinkedListSetLib.sol | 9 ++++++++- src/libraries/LICENSE | 21 ++++++++++++++++++++ src/libraries/LinkedListSetLib.sol | 9 ++++++++- src/libraries/PluginStorageLib.sol | 9 ++++++++- 7 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 src/libraries/LICENSE diff --git a/README.md b/README.md index 9822f627..ef7fd1af 100644 --- a/README.md +++ b/README.md @@ -58,3 +58,9 @@ We use Solady's highly optimized [UUPSUpgradeable](https://github.com/Vectorized ```bash forge script script/Deploy.s.sol --rpc-url $SEPOLIA_RPC_URL --broadcast ``` + +## License + +The Modular Account libraries (i.e., all code inside the [src/libraries](src/libraries) directory) is licensed under the MIT License, also included in our repository in [src/libraries/LICENSE](src/libraries/LICENSE). + +Alchemy Insights, Inc., 548 Market St., PMB 49099, San Francisco, CA 94104; legal@alchemy.com diff --git a/src/libraries/AssociatedLinkedListSetLib.sol b/src/libraries/AssociatedLinkedListSetLib.sol index 2b9ace6d..66ed93bb 100644 --- a/src/libraries/AssociatedLinkedListSetLib.sol +++ b/src/libraries/AssociatedLinkedListSetLib.sol @@ -1,4 +1,11 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: MIT +// +// See LICENSE file for more information + pragma solidity ^0.8.22; import {SetValue, SENTINEL_VALUE, HAS_NEXT_FLAG} from "./Constants.sol"; diff --git a/src/libraries/Constants.sol b/src/libraries/Constants.sol index 59159d4a..45644c49 100644 --- a/src/libraries/Constants.sol +++ b/src/libraries/Constants.sol @@ -1,4 +1,11 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: MIT +// +// See LICENSE file for more information + pragma solidity ^0.8.22; type SetValue is bytes30; diff --git a/src/libraries/CountableLinkedListSetLib.sol b/src/libraries/CountableLinkedListSetLib.sol index 8b84da91..2945d6ef 100644 --- a/src/libraries/CountableLinkedListSetLib.sol +++ b/src/libraries/CountableLinkedListSetLib.sol @@ -1,4 +1,11 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: MIT +// +// See LICENSE file for more information + pragma solidity ^0.8.22; import {SetValue} from "./Constants.sol"; diff --git a/src/libraries/LICENSE b/src/libraries/LICENSE new file mode 100644 index 00000000..a2df6853 --- /dev/null +++ b/src/libraries/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Alchemy Insights, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/libraries/LinkedListSetLib.sol b/src/libraries/LinkedListSetLib.sol index 5a43190b..081aab87 100644 --- a/src/libraries/LinkedListSetLib.sol +++ b/src/libraries/LinkedListSetLib.sol @@ -1,4 +1,11 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: MIT +// +// See LICENSE file for more information + pragma solidity ^0.8.22; import {SetValue, SENTINEL_VALUE, HAS_NEXT_FLAG} from "./Constants.sol"; diff --git a/src/libraries/PluginStorageLib.sol b/src/libraries/PluginStorageLib.sol index 400b002a..f6db03f5 100644 --- a/src/libraries/PluginStorageLib.sol +++ b/src/libraries/PluginStorageLib.sol @@ -1,4 +1,11 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: MIT +// +// See LICENSE file for more information + pragma solidity ^0.8.22; type StoragePointer is bytes32; From 8ccfd462dd83182e0eb0291035a88d640fa5054a Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Tue, 23 Jan 2024 01:03:12 -0500 Subject: [PATCH 094/106] chore: move license to root (#109) --- src/libraries/LICENSE => LICENSE-MIT | 0 README.md | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/libraries/LICENSE => LICENSE-MIT (100%) diff --git a/src/libraries/LICENSE b/LICENSE-MIT similarity index 100% rename from src/libraries/LICENSE rename to LICENSE-MIT diff --git a/README.md b/README.md index ef7fd1af..d204c810 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,6 @@ forge script script/Deploy.s.sol --rpc-url $SEPOLIA_RPC_URL --broadcast ## License -The Modular Account libraries (i.e., all code inside the [src/libraries](src/libraries) directory) is licensed under the MIT License, also included in our repository in [src/libraries/LICENSE](src/libraries/LICENSE). +The Modular Account libraries (i.e., all code inside the [src/libraries](src/libraries) directory) are licensed under the MIT License, also included in our repository in [LICENSE-MIT](LICENSE-MIT). Alchemy Insights, Inc., 548 Market St., PMB 49099, San Francisco, CA 94104; legal@alchemy.com From 61921893816fbdc13bae42d68e82bd7468b46c39 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Tue, 23 Jan 2024 01:08:31 -0500 Subject: [PATCH 095/106] chore: add license for all other files outside of interfaces and libraries (#107) --- COPYING | 674 ++++++++++++++++++ README.md | 2 + script/Deploy.s.sol | 18 +- src/account/AccountExecutor.sol | 18 +- src/account/AccountLoupe.sol | 18 +- src/account/AccountStorageInitializable.sol | 18 +- src/account/AccountStorageV1.sol | 18 +- src/account/PluginManagerInternals.sol | 18 +- src/account/UpgradeableModularAccount.sol | 18 +- src/factory/MultiOwnerMSCAFactory.sol | 18 +- .../MultiOwnerTokenReceiverMSCAFactory.sol | 18 +- src/helpers/CastLib.sol | 18 +- src/helpers/FactoryHelpers.sol | 18 +- src/helpers/FunctionReferenceLib.sol | 18 +- src/helpers/KnownSelectors.sol | 18 +- src/helpers/ValidationDataHelpers.sol | 18 +- src/plugins/BasePlugin.sol | 18 +- src/plugins/TokenReceiverPlugin.sol | 18 +- src/plugins/owner/IMultiOwnerPlugin.sol | 18 +- src/plugins/owner/MultiOwnerPlugin.sol | 18 +- src/plugins/session/ISessionKeyPlugin.sol | 18 +- src/plugins/session/SessionKeyPlugin.sol | 18 +- .../ISessionKeyPermissionsUpdates.sol | 18 +- .../permissions/SessionKeyPermissions.sol | 18 +- .../permissions/SessionKeyPermissionsBase.sol | 18 +- .../SessionKeyPermissionsLoupe.sol | 18 +- test/TestUtils.sol | 18 +- test/Utils.sol | 18 +- test/account/AccountExecHooks.t.sol | 18 +- test/account/AccountLoupe.t.sol | 18 +- test/account/AccountPreValidationHooks.t.sol | 18 +- test/account/AccountReturnData.t.sol | 18 +- test/account/AccountStorage.t.sol | 18 +- .../ExecuteFromPluginPermissions.t.sol | 18 +- test/account/ManifestValidity.t.sol | 18 +- test/account/UpgradeableModularAccount.t.sol | 18 +- ...gradeableModularAccountPluginManager.t.sol | 18 +- test/account/ValidationIntersection.t.sol | 18 +- test/account/phases/AccountStatePhases.t.sol | 18 +- .../phases/AccountStatePhasesExec.t.sol | 18 +- .../AccountStatePhasesRTValidation.t.sol | 18 +- .../AccountStatePhasesUOValidation.t.sol | 18 +- test/comparison/CompareSimpleAccount.t.sol | 18 +- test/factory/MultiOwnerMSCAFactoryTest.t.sol | 18 +- .../MultiOwnerTokenReceiverFactoryTest.t.sol | 18 +- test/helpers/FunctionReferenceLib.t.sol | 18 +- test/helpers/KnownSelectors.t.sol | 18 +- ...AssociatedLinkedListSetLibInvariants.t.sol | 18 +- test/invariant/LLSLRepro.t.sol | 18 +- .../LinkedListSetLibInvariants.t.sol | 18 +- .../AssociatedLinkedListSetHandler.sol | 18 +- .../handlers/LinkedListSetHandler.sol | 18 +- .../AssociatedLinkedListSetLib.t.sol | 18 +- .../libraries/CountableLinkedListSetLib.t.sol | 18 +- test/libraries/LinkedListSetLib.t.sol | 18 +- test/libraries/PluginStorageLib.t.sol | 18 +- test/mocks/ContractOwner.sol | 18 +- test/mocks/Counter.sol | 18 +- test/mocks/Counter.t.sol | 18 +- test/mocks/MockDiamondStorageContract.sol | 18 +- test/mocks/MockPlugin.sol | 18 +- .../plugins/AccountStateMutatingPlugin.sol | 18 +- .../plugins/BadTransferOwnershipPlugin.sol | 18 +- test/mocks/plugins/BaseTestPlugin.sol | 18 +- test/mocks/plugins/ChangingManifestPlugin.sol | 18 +- test/mocks/plugins/ComprehensivePlugin.sol | 18 +- .../ExecFromPluginPermissionsMocks.sol | 18 +- test/mocks/plugins/ManifestValidityMocks.sol | 18 +- test/mocks/plugins/ReturnDataPluginMocks.sol | 18 +- test/mocks/plugins/UninstallErrorsPlugin.sol | 18 +- test/mocks/plugins/ValidationPluginMocks.sol | 18 +- test/mocks/tokens/MockERC1155.sol | 18 +- test/mocks/tokens/MockERC20.sol | 18 +- test/mocks/tokens/MockERC777.sol | 18 +- test/plugin/TokenReceiverPlugin.t.sol | 18 +- test/plugin/owner/MultiOwnerPlugin.t.sol | 18 +- .../owner/MultiOwnerPluginIntegration.t.sol | 18 +- .../SessionKeyPluginWithMultiOwner.t.sol | 18 +- .../SessionKeyERC20SpendLimits.t.sol | 18 +- .../permissions/SessionKeyGasLimits.t.sol | 18 +- .../SessionKeyNativeTokenSpendLimits.t.sol | 18 +- .../permissions/SessionKeyPermissions.t.sol | 18 +- test/upgrade/LightAccountToMSCA.t.sol | 18 +- test/upgrade/MSCAToMSCA.t.sol | 18 +- 84 files changed, 2070 insertions(+), 82 deletions(-) create mode 100644 COPYING diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..f288702d --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md index d204c810..af46e288 100644 --- a/README.md +++ b/README.md @@ -63,4 +63,6 @@ forge script script/Deploy.s.sol --rpc-url $SEPOLIA_RPC_URL --broadcast The Modular Account libraries (i.e., all code inside the [src/libraries](src/libraries) directory) are licensed under the MIT License, also included in our repository in [LICENSE-MIT](LICENSE-MIT). +All other code for Modular Account is licensed under the GNU General Public License v3.0, also included in our repository in [COPYING](COPYING). + Alchemy Insights, Inc., 548 Market St., PMB 49099, San Francisco, CA 94104; legal@alchemy.com diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index 939f3643..18e6dcb3 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {console} from "forge-std/Test.sol"; diff --git a/src/account/AccountExecutor.sol b/src/account/AccountExecutor.sol index bbfbd305..ace020c2 100644 --- a/src/account/AccountExecutor.sol +++ b/src/account/AccountExecutor.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; diff --git a/src/account/AccountLoupe.sol b/src/account/AccountLoupe.sol index 5534951a..0e6b3a51 100644 --- a/src/account/AccountLoupe.sol +++ b/src/account/AccountLoupe.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {AccountStorageV1} from "../account/AccountStorageV1.sol"; diff --git a/src/account/AccountStorageInitializable.sol b/src/account/AccountStorageInitializable.sol index 2230a989..23eb67ca 100644 --- a/src/account/AccountStorageInitializable.sol +++ b/src/account/AccountStorageInitializable.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: MIT +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; diff --git a/src/account/AccountStorageV1.sol b/src/account/AccountStorageV1.sol index ceefd78c..f6c38f8b 100644 --- a/src/account/AccountStorageV1.sol +++ b/src/account/AccountStorageV1.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {IPlugin} from "../interfaces/IPlugin.sol"; diff --git a/src/account/PluginManagerInternals.sol b/src/account/PluginManagerInternals.sol index d8d3585c..b1abe5cf 100644 --- a/src/account/PluginManagerInternals.sol +++ b/src/account/PluginManagerInternals.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index 5bb9488f..b92e94e7 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; diff --git a/src/factory/MultiOwnerMSCAFactory.sol b/src/factory/MultiOwnerMSCAFactory.sol index 47a76915..b1a2e368 100644 --- a/src/factory/MultiOwnerMSCAFactory.sol +++ b/src/factory/MultiOwnerMSCAFactory.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol"; diff --git a/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol b/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol index d204bd1a..44925079 100644 --- a/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol +++ b/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol"; diff --git a/src/helpers/CastLib.sol b/src/helpers/CastLib.sol index 0e424ae8..04e32450 100644 --- a/src/helpers/CastLib.sol +++ b/src/helpers/CastLib.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {SetValue} from "../libraries/Constants.sol"; diff --git a/src/helpers/FactoryHelpers.sol b/src/helpers/FactoryHelpers.sol index 7f53870c..cdbaab9b 100644 --- a/src/helpers/FactoryHelpers.sol +++ b/src/helpers/FactoryHelpers.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; library FactoryHelpers { diff --git a/src/helpers/FunctionReferenceLib.sol b/src/helpers/FunctionReferenceLib.sol index 5f5e5466..e77e3a09 100644 --- a/src/helpers/FunctionReferenceLib.sol +++ b/src/helpers/FunctionReferenceLib.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {FunctionReference} from "../interfaces/IPluginManager.sol"; diff --git a/src/helpers/KnownSelectors.sol b/src/helpers/KnownSelectors.sol index 6c5efc39..77e48052 100644 --- a/src/helpers/KnownSelectors.sol +++ b/src/helpers/KnownSelectors.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {UUPSUpgradeable} from "../../ext/UUPSUpgradeable.sol"; diff --git a/src/helpers/ValidationDataHelpers.sol b/src/helpers/ValidationDataHelpers.sol index ba9a3630..7cf75da9 100644 --- a/src/helpers/ValidationDataHelpers.sol +++ b/src/helpers/ValidationDataHelpers.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {SIG_VALIDATION_FAILED} from "../libraries/Constants.sol"; diff --git a/src/plugins/BasePlugin.sol b/src/plugins/BasePlugin.sol index e873e596..c9239819 100644 --- a/src/plugins/BasePlugin.sol +++ b/src/plugins/BasePlugin.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; diff --git a/src/plugins/TokenReceiverPlugin.sol b/src/plugins/TokenReceiverPlugin.sol index a1e3b033..d4e528c2 100644 --- a/src/plugins/TokenReceiverPlugin.sol +++ b/src/plugins/TokenReceiverPlugin.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {IERC1155Receiver} from "@openzeppelin/contracts/interfaces/IERC1155Receiver.sol"; diff --git a/src/plugins/owner/IMultiOwnerPlugin.sol b/src/plugins/owner/IMultiOwnerPlugin.sol index 37d24775..edfe138b 100644 --- a/src/plugins/owner/IMultiOwnerPlugin.sol +++ b/src/plugins/owner/IMultiOwnerPlugin.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {UserOperation} from "../../interfaces/erc4337/UserOperation.sol"; diff --git a/src/plugins/owner/MultiOwnerPlugin.sol b/src/plugins/owner/MultiOwnerPlugin.sol index fe72aa58..ff9a6d69 100644 --- a/src/plugins/owner/MultiOwnerPlugin.sol +++ b/src/plugins/owner/MultiOwnerPlugin.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol"; diff --git a/src/plugins/session/ISessionKeyPlugin.sol b/src/plugins/session/ISessionKeyPlugin.sol index bb7cd040..2c2228e9 100644 --- a/src/plugins/session/ISessionKeyPlugin.sol +++ b/src/plugins/session/ISessionKeyPlugin.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {UserOperation} from "../../interfaces/erc4337/UserOperation.sol"; diff --git a/src/plugins/session/SessionKeyPlugin.sol b/src/plugins/session/SessionKeyPlugin.sol index b2b28495..7960b3f8 100644 --- a/src/plugins/session/SessionKeyPlugin.sol +++ b/src/plugins/session/SessionKeyPlugin.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; diff --git a/src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol b/src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol index aec02143..b6d63c92 100644 --- a/src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol +++ b/src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {ISessionKeyPlugin} from "../ISessionKeyPlugin.sol"; diff --git a/src/plugins/session/permissions/SessionKeyPermissions.sol b/src/plugins/session/permissions/SessionKeyPermissions.sol index dbfa56a7..6adbedff 100644 --- a/src/plugins/session/permissions/SessionKeyPermissions.sol +++ b/src/plugins/session/permissions/SessionKeyPermissions.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; diff --git a/src/plugins/session/permissions/SessionKeyPermissionsBase.sol b/src/plugins/session/permissions/SessionKeyPermissionsBase.sol index 5519944b..ba9bd955 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsBase.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsBase.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {PluginStorageLib, StoragePointer} from "../../../libraries/PluginStorageLib.sol"; diff --git a/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol b/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol index 850e2106..7c1c5910 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {ISessionKeyPlugin} from "../ISessionKeyPlugin.sol"; diff --git a/test/TestUtils.sol b/test/TestUtils.sol index 96266376..87d79ab1 100644 --- a/test/TestUtils.sol +++ b/test/TestUtils.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test, console} from "forge-std/Test.sol"; diff --git a/test/Utils.sol b/test/Utils.sol index e5297452..5609d1fd 100644 --- a/test/Utils.sol +++ b/test/Utils.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; library Utils { diff --git a/test/account/AccountExecHooks.t.sol b/test/account/AccountExecHooks.t.sol index e3ae71e1..692d2160 100644 --- a/test/account/AccountExecHooks.t.sol +++ b/test/account/AccountExecHooks.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/account/AccountLoupe.t.sol b/test/account/AccountLoupe.t.sol index 2b5ca631..3e9b1814 100644 --- a/test/account/AccountLoupe.t.sol +++ b/test/account/AccountLoupe.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/account/AccountPreValidationHooks.t.sol b/test/account/AccountPreValidationHooks.t.sol index 9c49e6a4..103c66da 100644 --- a/test/account/AccountPreValidationHooks.t.sol +++ b/test/account/AccountPreValidationHooks.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/account/AccountReturnData.t.sol b/test/account/AccountReturnData.t.sol index 4e27426c..4467663e 100644 --- a/test/account/AccountReturnData.t.sol +++ b/test/account/AccountReturnData.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/account/AccountStorage.t.sol b/test/account/AccountStorage.t.sol index 33091a04..c02c0cb5 100644 --- a/test/account/AccountStorage.t.sol +++ b/test/account/AccountStorage.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/account/ExecuteFromPluginPermissions.t.sol b/test/account/ExecuteFromPluginPermissions.t.sol index f540e8b9..be9a67be 100644 --- a/test/account/ExecuteFromPluginPermissions.t.sol +++ b/test/account/ExecuteFromPluginPermissions.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test, console} from "forge-std/Test.sol"; diff --git a/test/account/ManifestValidity.t.sol b/test/account/ManifestValidity.t.sol index 116a4022..1ea54f17 100644 --- a/test/account/ManifestValidity.t.sol +++ b/test/account/ManifestValidity.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/account/UpgradeableModularAccount.t.sol b/test/account/UpgradeableModularAccount.t.sol index f28729a6..37f64085 100644 --- a/test/account/UpgradeableModularAccount.t.sol +++ b/test/account/UpgradeableModularAccount.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test, console} from "forge-std/Test.sol"; diff --git a/test/account/UpgradeableModularAccountPluginManager.t.sol b/test/account/UpgradeableModularAccountPluginManager.t.sol index e96263c1..a5e24733 100644 --- a/test/account/UpgradeableModularAccountPluginManager.t.sol +++ b/test/account/UpgradeableModularAccountPluginManager.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/account/ValidationIntersection.t.sol b/test/account/ValidationIntersection.t.sol index 7c3214fb..6a71577f 100644 --- a/test/account/ValidationIntersection.t.sol +++ b/test/account/ValidationIntersection.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/account/phases/AccountStatePhases.t.sol b/test/account/phases/AccountStatePhases.t.sol index eebdbf16..0abc9e63 100644 --- a/test/account/phases/AccountStatePhases.t.sol +++ b/test/account/phases/AccountStatePhases.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/account/phases/AccountStatePhasesExec.t.sol b/test/account/phases/AccountStatePhasesExec.t.sol index 07551e78..0bfeb126 100644 --- a/test/account/phases/AccountStatePhasesExec.t.sol +++ b/test/account/phases/AccountStatePhasesExec.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import { diff --git a/test/account/phases/AccountStatePhasesRTValidation.t.sol b/test/account/phases/AccountStatePhasesRTValidation.t.sol index f572baaf..f61f244e 100644 --- a/test/account/phases/AccountStatePhasesRTValidation.t.sol +++ b/test/account/phases/AccountStatePhasesRTValidation.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {UpgradeableModularAccount} from "../../../src/account/UpgradeableModularAccount.sol"; diff --git a/test/account/phases/AccountStatePhasesUOValidation.t.sol b/test/account/phases/AccountStatePhasesUOValidation.t.sol index e0e79d7d..88e6b57a 100644 --- a/test/account/phases/AccountStatePhasesUOValidation.t.sol +++ b/test/account/phases/AccountStatePhasesUOValidation.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {AccountStatePhasesTest} from "./AccountStatePhases.t.sol"; diff --git a/test/comparison/CompareSimpleAccount.t.sol b/test/comparison/CompareSimpleAccount.t.sol index e48c2d3e..0e7945a3 100644 --- a/test/comparison/CompareSimpleAccount.t.sol +++ b/test/comparison/CompareSimpleAccount.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/factory/MultiOwnerMSCAFactoryTest.t.sol b/test/factory/MultiOwnerMSCAFactoryTest.t.sol index 07628e3f..a47f7f6f 100644 --- a/test/factory/MultiOwnerMSCAFactoryTest.t.sol +++ b/test/factory/MultiOwnerMSCAFactoryTest.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol b/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol index d771ca33..71eccacb 100644 --- a/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol +++ b/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/helpers/FunctionReferenceLib.t.sol b/test/helpers/FunctionReferenceLib.t.sol index e72dcb41..f30f3fe7 100644 --- a/test/helpers/FunctionReferenceLib.t.sol +++ b/test/helpers/FunctionReferenceLib.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/helpers/KnownSelectors.t.sol b/test/helpers/KnownSelectors.t.sol index 978e3dbd..f8db7dc9 100644 --- a/test/helpers/KnownSelectors.t.sol +++ b/test/helpers/KnownSelectors.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/invariant/AssociatedLinkedListSetLibInvariants.t.sol b/test/invariant/AssociatedLinkedListSetLibInvariants.t.sol index a0208cde..cfe6a75f 100644 --- a/test/invariant/AssociatedLinkedListSetLibInvariants.t.sol +++ b/test/invariant/AssociatedLinkedListSetLibInvariants.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/invariant/LLSLRepro.t.sol b/test/invariant/LLSLRepro.t.sol index 6d9984c5..518d4c52 100644 --- a/test/invariant/LLSLRepro.t.sol +++ b/test/invariant/LLSLRepro.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/invariant/LinkedListSetLibInvariants.t.sol b/test/invariant/LinkedListSetLibInvariants.t.sol index b6787cfd..fd8243fd 100644 --- a/test/invariant/LinkedListSetLibInvariants.t.sol +++ b/test/invariant/LinkedListSetLibInvariants.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/invariant/handlers/AssociatedLinkedListSetHandler.sol b/test/invariant/handlers/AssociatedLinkedListSetHandler.sol index ffbec216..f3814a8c 100644 --- a/test/invariant/handlers/AssociatedLinkedListSetHandler.sol +++ b/test/invariant/handlers/AssociatedLinkedListSetHandler.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {CommonBase} from "forge-std/Base.sol"; diff --git a/test/invariant/handlers/LinkedListSetHandler.sol b/test/invariant/handlers/LinkedListSetHandler.sol index a3d59bc4..56b34dac 100644 --- a/test/invariant/handlers/LinkedListSetHandler.sol +++ b/test/invariant/handlers/LinkedListSetHandler.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {CommonBase} from "forge-std/Base.sol"; diff --git a/test/libraries/AssociatedLinkedListSetLib.t.sol b/test/libraries/AssociatedLinkedListSetLib.t.sol index 748081f4..3930ad1f 100644 --- a/test/libraries/AssociatedLinkedListSetLib.t.sol +++ b/test/libraries/AssociatedLinkedListSetLib.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/libraries/CountableLinkedListSetLib.t.sol b/test/libraries/CountableLinkedListSetLib.t.sol index 0c57faae..d7ead39b 100644 --- a/test/libraries/CountableLinkedListSetLib.t.sol +++ b/test/libraries/CountableLinkedListSetLib.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/libraries/LinkedListSetLib.t.sol b/test/libraries/LinkedListSetLib.t.sol index 73a39b95..8c89540b 100644 --- a/test/libraries/LinkedListSetLib.t.sol +++ b/test/libraries/LinkedListSetLib.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/libraries/PluginStorageLib.t.sol b/test/libraries/PluginStorageLib.t.sol index 8640b407..5ae19e81 100644 --- a/test/libraries/PluginStorageLib.t.sol +++ b/test/libraries/PluginStorageLib.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/mocks/ContractOwner.sol b/test/mocks/ContractOwner.sol index 35704e19..9a639249 100644 --- a/test/mocks/ContractOwner.sol +++ b/test/mocks/ContractOwner.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol"; diff --git a/test/mocks/Counter.sol b/test/mocks/Counter.sol index 3ee416bb..b2eb37d9 100644 --- a/test/mocks/Counter.sol +++ b/test/mocks/Counter.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; /// @title A public counter for anyone to use. diff --git a/test/mocks/Counter.t.sol b/test/mocks/Counter.t.sol index 70f98df7..5c8f5147 100644 --- a/test/mocks/Counter.t.sol +++ b/test/mocks/Counter.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/mocks/MockDiamondStorageContract.sol b/test/mocks/MockDiamondStorageContract.sol index e71e4379..428f604a 100644 --- a/test/mocks/MockDiamondStorageContract.sol +++ b/test/mocks/MockDiamondStorageContract.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {AccountStorageInitializable} from "../../src/account/AccountStorageInitializable.sol"; diff --git a/test/mocks/MockPlugin.sol b/test/mocks/MockPlugin.sol index 3d7e4260..94ccfa3b 100644 --- a/test/mocks/MockPlugin.sol +++ b/test/mocks/MockPlugin.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; diff --git a/test/mocks/plugins/AccountStateMutatingPlugin.sol b/test/mocks/plugins/AccountStateMutatingPlugin.sol index 1480d91d..6bf14797 100644 --- a/test/mocks/plugins/AccountStateMutatingPlugin.sol +++ b/test/mocks/plugins/AccountStateMutatingPlugin.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {UserOperation} from "../../../src/interfaces/erc4337/UserOperation.sol"; diff --git a/test/mocks/plugins/BadTransferOwnershipPlugin.sol b/test/mocks/plugins/BadTransferOwnershipPlugin.sol index 27a5e07f..a8b52050 100644 --- a/test/mocks/plugins/BadTransferOwnershipPlugin.sol +++ b/test/mocks/plugins/BadTransferOwnershipPlugin.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import { diff --git a/test/mocks/plugins/BaseTestPlugin.sol b/test/mocks/plugins/BaseTestPlugin.sol index cb28d43d..33c26fa6 100644 --- a/test/mocks/plugins/BaseTestPlugin.sol +++ b/test/mocks/plugins/BaseTestPlugin.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {PluginMetadata} from "../../../src/interfaces/IPlugin.sol"; diff --git a/test/mocks/plugins/ChangingManifestPlugin.sol b/test/mocks/plugins/ChangingManifestPlugin.sol index 7c798869..58abcf8a 100644 --- a/test/mocks/plugins/ChangingManifestPlugin.sol +++ b/test/mocks/plugins/ChangingManifestPlugin.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; diff --git a/test/mocks/plugins/ComprehensivePlugin.sol b/test/mocks/plugins/ComprehensivePlugin.sol index 471ecf4a..9671e17e 100644 --- a/test/mocks/plugins/ComprehensivePlugin.sol +++ b/test/mocks/plugins/ComprehensivePlugin.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {UserOperation} from "../../../src/interfaces/erc4337/UserOperation.sol"; diff --git a/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol b/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol index 3db37e36..27b44c62 100644 --- a/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol +++ b/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import { diff --git a/test/mocks/plugins/ManifestValidityMocks.sol b/test/mocks/plugins/ManifestValidityMocks.sol index 1cb17ee3..30990a67 100644 --- a/test/mocks/plugins/ManifestValidityMocks.sol +++ b/test/mocks/plugins/ManifestValidityMocks.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import { diff --git a/test/mocks/plugins/ReturnDataPluginMocks.sol b/test/mocks/plugins/ReturnDataPluginMocks.sol index a1cc4da7..2d25a3fd 100644 --- a/test/mocks/plugins/ReturnDataPluginMocks.sol +++ b/test/mocks/plugins/ReturnDataPluginMocks.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import { diff --git a/test/mocks/plugins/UninstallErrorsPlugin.sol b/test/mocks/plugins/UninstallErrorsPlugin.sol index 60731795..c9448c28 100644 --- a/test/mocks/plugins/UninstallErrorsPlugin.sol +++ b/test/mocks/plugins/UninstallErrorsPlugin.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {StorageSlot} from "@openzeppelin/contracts/utils/StorageSlot.sol"; diff --git a/test/mocks/plugins/ValidationPluginMocks.sol b/test/mocks/plugins/ValidationPluginMocks.sol index 1f899217..bcc80069 100644 --- a/test/mocks/plugins/ValidationPluginMocks.sol +++ b/test/mocks/plugins/ValidationPluginMocks.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {UserOperation} from "../../../src/interfaces/erc4337/UserOperation.sol"; diff --git a/test/mocks/tokens/MockERC1155.sol b/test/mocks/tokens/MockERC1155.sol index 2ec184a7..6f54cec8 100644 --- a/test/mocks/tokens/MockERC1155.sol +++ b/test/mocks/tokens/MockERC1155.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; diff --git a/test/mocks/tokens/MockERC20.sol b/test/mocks/tokens/MockERC20.sol index 10b9451f..619d7171 100644 --- a/test/mocks/tokens/MockERC20.sol +++ b/test/mocks/tokens/MockERC20.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; diff --git a/test/mocks/tokens/MockERC777.sol b/test/mocks/tokens/MockERC777.sol index 913d7795..9bbb66ff 100644 --- a/test/mocks/tokens/MockERC777.sol +++ b/test/mocks/tokens/MockERC777.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {IERC777} from "@openzeppelin/contracts/token/ERC777/IERC777.sol"; diff --git a/test/plugin/TokenReceiverPlugin.t.sol b/test/plugin/TokenReceiverPlugin.t.sol index 4fa9ecfc..9c2ccffa 100644 --- a/test/plugin/TokenReceiverPlugin.t.sol +++ b/test/plugin/TokenReceiverPlugin.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/plugin/owner/MultiOwnerPlugin.t.sol b/test/plugin/owner/MultiOwnerPlugin.t.sol index ca784690..4ecab62d 100644 --- a/test/plugin/owner/MultiOwnerPlugin.t.sol +++ b/test/plugin/owner/MultiOwnerPlugin.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/plugin/owner/MultiOwnerPluginIntegration.t.sol b/test/plugin/owner/MultiOwnerPluginIntegration.t.sol index 23853523..71564fb1 100644 --- a/test/plugin/owner/MultiOwnerPluginIntegration.t.sol +++ b/test/plugin/owner/MultiOwnerPluginIntegration.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol index 83ecd853..e67fbbad 100644 --- a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol +++ b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol b/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol index 48bdf718..25132aa0 100644 --- a/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test, console} from "forge-std/Test.sol"; diff --git a/test/plugin/session/permissions/SessionKeyGasLimits.t.sol b/test/plugin/session/permissions/SessionKeyGasLimits.t.sol index 2f7d2b43..3ab483f4 100644 --- a/test/plugin/session/permissions/SessionKeyGasLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyGasLimits.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test, console} from "forge-std/Test.sol"; diff --git a/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol b/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol index b8e00fe8..5596fa47 100644 --- a/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test, console} from "forge-std/Test.sol"; diff --git a/test/plugin/session/permissions/SessionKeyPermissions.t.sol b/test/plugin/session/permissions/SessionKeyPermissions.t.sol index a8bbe8c4..7b17b93d 100644 --- a/test/plugin/session/permissions/SessionKeyPermissions.t.sol +++ b/test/plugin/session/permissions/SessionKeyPermissions.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/upgrade/LightAccountToMSCA.t.sol b/test/upgrade/LightAccountToMSCA.t.sol index 2fa335cc..e6f5c088 100644 --- a/test/upgrade/LightAccountToMSCA.t.sol +++ b/test/upgrade/LightAccountToMSCA.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; diff --git a/test/upgrade/MSCAToMSCA.t.sol b/test/upgrade/MSCAToMSCA.t.sol index 222a1294..a63fa9ef 100644 --- a/test/upgrade/MSCAToMSCA.t.sol +++ b/test/upgrade/MSCAToMSCA.t.sol @@ -1,4 +1,20 @@ -// SPDX-License-Identifier: UNLICENSED +// This file is part of Modular Account. +// +// Copyright 2024 Alchemy Insights, Inc. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General +// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with this program. If not, see +// . + pragma solidity ^0.8.22; import {Test} from "forge-std/Test.sol"; From e68ad9aae20c71fe30089525c503dfa9e47c441e Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Tue, 23 Jan 2024 01:21:00 -0500 Subject: [PATCH 096/106] chore: add license to interfaces (#110) --- LICENSE-CC0 | 121 +++++++++++++++++++ README.md | 2 + src/interfaces/IAccountInitializable.sol | 2 +- src/interfaces/IAccountLoupe.sol | 2 +- src/interfaces/IAccountView.sol | 2 +- src/interfaces/IPlugin.sol | 2 +- src/interfaces/IPluginExecutor.sol | 2 +- src/interfaces/IPluginManager.sol | 2 +- src/interfaces/IStandardExecutor.sol | 2 +- src/interfaces/erc4337/IAccount.sol | 2 +- src/interfaces/erc4337/IAggregator.sol | 2 +- src/interfaces/erc4337/IEntryPoint.sol | 2 +- src/interfaces/erc4337/IPaymaster.sol | 2 +- src/interfaces/erc4337/UserOperation.sol | 2 +- src/libraries/AssociatedLinkedListSetLib.sol | 2 +- src/libraries/Constants.sol | 2 +- src/libraries/CountableLinkedListSetLib.sol | 2 +- src/libraries/LinkedListSetLib.sol | 2 +- src/libraries/PluginStorageLib.sol | 2 +- 19 files changed, 140 insertions(+), 17 deletions(-) create mode 100644 LICENSE-CC0 diff --git a/LICENSE-CC0 b/LICENSE-CC0 new file mode 100644 index 00000000..0e259d42 --- /dev/null +++ b/LICENSE-CC0 @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/README.md b/README.md index af46e288..32b3f13b 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,8 @@ forge script script/Deploy.s.sol --rpc-url $SEPOLIA_RPC_URL --broadcast The Modular Account libraries (i.e., all code inside the [src/libraries](src/libraries) directory) are licensed under the MIT License, also included in our repository in [LICENSE-MIT](LICENSE-MIT). +The Modular Account and ERC-6900 interfaces (i.e., all code inside the [src/interfaces](src/interfaces) directory) are licensed under the CC0 1.0 Universal License, also included in our repository in [LICENSE-CC0](LICENSE-CC0). + All other code for Modular Account is licensed under the GNU General Public License v3.0, also included in our repository in [COPYING](COPYING). Alchemy Insights, Inc., 548 Market St., PMB 49099, San Francisco, CA 94104; legal@alchemy.com diff --git a/src/interfaces/IAccountInitializable.sol b/src/interfaces/IAccountInitializable.sol index 335db1cf..195e9835 100644 --- a/src/interfaces/IAccountInitializable.sol +++ b/src/interfaces/IAccountInitializable.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.22; /// @title Account Initializable Interface diff --git a/src/interfaces/IAccountLoupe.sol b/src/interfaces/IAccountLoupe.sol index ed794cc9..678d808a 100644 --- a/src/interfaces/IAccountLoupe.sol +++ b/src/interfaces/IAccountLoupe.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.22; import {FunctionReference} from "./IPluginManager.sol"; diff --git a/src/interfaces/IAccountView.sol b/src/interfaces/IAccountView.sol index 7f034576..8c339163 100644 --- a/src/interfaces/IAccountView.sol +++ b/src/interfaces/IAccountView.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.22; import {IEntryPoint} from "./erc4337/IEntryPoint.sol"; diff --git a/src/interfaces/IPlugin.sol b/src/interfaces/IPlugin.sol index 1d6e292d..59f57f9b 100644 --- a/src/interfaces/IPlugin.sol +++ b/src/interfaces/IPlugin.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.22; import {UserOperation} from "../interfaces/erc4337/UserOperation.sol"; diff --git a/src/interfaces/IPluginExecutor.sol b/src/interfaces/IPluginExecutor.sol index 88437749..2db6fb8e 100644 --- a/src/interfaces/IPluginExecutor.sol +++ b/src/interfaces/IPluginExecutor.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.22; /// @title Plugin Executor Interface diff --git a/src/interfaces/IPluginManager.sol b/src/interfaces/IPluginManager.sol index 2d40634d..c926fdb0 100644 --- a/src/interfaces/IPluginManager.sol +++ b/src/interfaces/IPluginManager.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.22; type FunctionReference is bytes21; diff --git a/src/interfaces/IStandardExecutor.sol b/src/interfaces/IStandardExecutor.sol index 9284a1d7..741e284d 100644 --- a/src/interfaces/IStandardExecutor.sol +++ b/src/interfaces/IStandardExecutor.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.22; struct Call { diff --git a/src/interfaces/erc4337/IAccount.sol b/src/interfaces/erc4337/IAccount.sol index ec0999d0..ee6b5c23 100644 --- a/src/interfaces/erc4337/IAccount.sol +++ b/src/interfaces/erc4337/IAccount.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.22; import {IEntryPoint} from "./IEntryPoint.sol"; diff --git a/src/interfaces/erc4337/IAggregator.sol b/src/interfaces/erc4337/IAggregator.sol index f8c7e5a7..40ca2be2 100644 --- a/src/interfaces/erc4337/IAggregator.sol +++ b/src/interfaces/erc4337/IAggregator.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.22; import {UserOperation} from "./UserOperation.sol"; diff --git a/src/interfaces/erc4337/IEntryPoint.sol b/src/interfaces/erc4337/IEntryPoint.sol index 2da014e5..d0f83193 100644 --- a/src/interfaces/erc4337/IEntryPoint.sol +++ b/src/interfaces/erc4337/IEntryPoint.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.22; import {UserOperation} from "./UserOperation.sol"; diff --git a/src/interfaces/erc4337/IPaymaster.sol b/src/interfaces/erc4337/IPaymaster.sol index e1cd8cd3..94ad7fb3 100644 --- a/src/interfaces/erc4337/IPaymaster.sol +++ b/src/interfaces/erc4337/IPaymaster.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.22; import {UserOperation} from "./UserOperation.sol"; diff --git a/src/interfaces/erc4337/UserOperation.sol b/src/interfaces/erc4337/UserOperation.sol index 23fbbe07..2644fea7 100644 --- a/src/interfaces/erc4337/UserOperation.sol +++ b/src/interfaces/erc4337/UserOperation.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.22; /// @notice User Operation struct as defined in ERC-4337 diff --git a/src/libraries/AssociatedLinkedListSetLib.sol b/src/libraries/AssociatedLinkedListSetLib.sol index 66ed93bb..963490b3 100644 --- a/src/libraries/AssociatedLinkedListSetLib.sol +++ b/src/libraries/AssociatedLinkedListSetLib.sol @@ -4,7 +4,7 @@ // // SPDX-License-Identifier: MIT // -// See LICENSE file for more information +// See LICENSE-MIT file for more information pragma solidity ^0.8.22; diff --git a/src/libraries/Constants.sol b/src/libraries/Constants.sol index 45644c49..192a095d 100644 --- a/src/libraries/Constants.sol +++ b/src/libraries/Constants.sol @@ -4,7 +4,7 @@ // // SPDX-License-Identifier: MIT // -// See LICENSE file for more information +// See LICENSE-MIT file for more information pragma solidity ^0.8.22; diff --git a/src/libraries/CountableLinkedListSetLib.sol b/src/libraries/CountableLinkedListSetLib.sol index 2945d6ef..a29a5aa8 100644 --- a/src/libraries/CountableLinkedListSetLib.sol +++ b/src/libraries/CountableLinkedListSetLib.sol @@ -4,7 +4,7 @@ // // SPDX-License-Identifier: MIT // -// See LICENSE file for more information +// See LICENSE-MIT file for more information pragma solidity ^0.8.22; diff --git a/src/libraries/LinkedListSetLib.sol b/src/libraries/LinkedListSetLib.sol index 081aab87..6b3b68cc 100644 --- a/src/libraries/LinkedListSetLib.sol +++ b/src/libraries/LinkedListSetLib.sol @@ -4,7 +4,7 @@ // // SPDX-License-Identifier: MIT // -// See LICENSE file for more information +// See LICENSE-MIT file for more information pragma solidity ^0.8.22; diff --git a/src/libraries/PluginStorageLib.sol b/src/libraries/PluginStorageLib.sol index f6db03f5..25f952c8 100644 --- a/src/libraries/PluginStorageLib.sol +++ b/src/libraries/PluginStorageLib.sol @@ -4,7 +4,7 @@ // // SPDX-License-Identifier: MIT // -// See LICENSE file for more information +// See LICENSE-MIT file for more information pragma solidity ^0.8.22; From a9da8fc847e8efdbf38d0263d40b3da8ff7446e2 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Tue, 23 Jan 2024 03:00:48 -0500 Subject: [PATCH 097/106] fix: update interface comments and pluginInitData parameter name (#108) --- src/account/PluginManagerInternals.sol | 4 +- src/account/UpgradeableModularAccount.sol | 4 +- src/interfaces/IAccountInitializable.sol | 8 +-- src/interfaces/IAccountLoupe.sol | 32 ++++++------ src/interfaces/IAccountView.sol | 6 +-- src/interfaces/IPlugin.sol | 52 ++++++++++--------- src/interfaces/IPluginExecutor.sol | 18 +++---- src/interfaces/IPluginManager.sol | 10 ++-- src/interfaces/IStandardExecutor.sol | 12 ++--- test/account/AccountExecHooks.t.sol | 4 +- test/account/AccountPreValidationHooks.t.sol | 8 +-- test/account/AccountReturnData.t.sol | 4 +- .../ExecuteFromPluginPermissions.t.sol | 10 ++-- test/account/ManifestValidity.t.sol | 16 +++--- test/account/UpgradeableModularAccount.t.sol | 2 +- ...gradeableModularAccountPluginManager.t.sol | 45 ++++++++-------- test/account/ValidationIntersection.t.sol | 6 +-- .../SessionKeyPluginWithMultiOwner.t.sol | 4 +- .../SessionKeyERC20SpendLimits.t.sol | 2 +- .../permissions/SessionKeyGasLimits.t.sol | 2 +- .../SessionKeyNativeTokenSpendLimits.t.sol | 2 +- .../permissions/SessionKeyPermissions.t.sol | 6 +-- 22 files changed, 129 insertions(+), 128 deletions(-) diff --git a/src/account/PluginManagerInternals.sol b/src/account/PluginManagerInternals.sol index b1abe5cf..56efbf29 100644 --- a/src/account/PluginManagerInternals.sol +++ b/src/account/PluginManagerInternals.sol @@ -289,7 +289,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { function _installPlugin( address plugin, bytes32 manifestHash, - bytes memory pluginInitData, + bytes memory pluginInstallData, FunctionReference[] memory dependencies ) internal { AccountStorage storage storage_ = _getAccountStorage(); @@ -469,7 +469,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { // Initialize the plugin storage for the account. // solhint-disable-next-line no-empty-blocks - try IPlugin(plugin).onInstall(pluginInitData) {} + try IPlugin(plugin).onInstall(pluginInstallData) {} catch (bytes memory revertReason) { revert PluginInstallCallbackFailed(plugin, revertReason); } diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index b92e94e7..fdd030e9 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -330,11 +330,11 @@ contract UpgradeableModularAccount is function installPlugin( address plugin, bytes32 manifestHash, - bytes calldata pluginInitData, + bytes calldata pluginInstallData, FunctionReference[] calldata dependencies ) external override { (FunctionReference[][] memory postExecHooks, bytes[] memory postHookArgs) = _preNativeFunction(); - _installPlugin(plugin, manifestHash, pluginInitData, dependencies); + _installPlugin(plugin, manifestHash, pluginInstallData, dependencies); _postNativeFunction(postExecHooks, postHookArgs); } diff --git a/src/interfaces/IAccountInitializable.sol b/src/interfaces/IAccountInitializable.sol index 195e9835..75236bdd 100644 --- a/src/interfaces/IAccountInitializable.sol +++ b/src/interfaces/IAccountInitializable.sol @@ -3,9 +3,9 @@ pragma solidity ^0.8.22; /// @title Account Initializable Interface interface IAccountInitializable { - /// @notice Initializes the account with a set of plugins - /// @dev No dependencies or hooks can be injected with this installation - /// @param plugins The plugins to install - /// @param pluginInitData The plugin init data for each plugin + /// @notice Initialize the account with a set of plugins. + /// @dev No dependencies or hooks can be injected with this installation. + /// @param plugins The plugins to install. + /// @param pluginInitData The plugin init data for each plugin. function initialize(address[] calldata plugins, bytes calldata pluginInitData) external; } diff --git a/src/interfaces/IAccountLoupe.sol b/src/interfaces/IAccountLoupe.sol index 678d808a..cb215478 100644 --- a/src/interfaces/IAccountLoupe.sol +++ b/src/interfaces/IAccountLoupe.sol @@ -5,35 +5,35 @@ import {FunctionReference} from "./IPluginManager.sol"; /// @title Account Loupe Interface interface IAccountLoupe { - /// @notice Config for an execution function, given a selector + /// @notice Config for an execution function, given a selector. struct ExecutionFunctionConfig { address plugin; FunctionReference userOpValidationFunction; FunctionReference runtimeValidationFunction; } - /// @notice Pre and post hooks for a given selector - /// @dev It's possible for one of either `preExecHook` or `postExecHook` to be empty + /// @notice Pre and post hooks for a given selector. + /// @dev It's possible for one of either `preExecHook` or `postExecHook` to be empty. struct ExecutionHooks { FunctionReference preExecHook; FunctionReference postExecHook; } - /// @notice Gets the validation functions and plugin address for a selector - /// @dev If the selector is a native function, the plugin address will be the address of the account - /// @param selector The selector to get the configuration for - /// @return The configuration for this selector + /// @notice Get the validation functions and plugin address for a selector. + /// @dev If the selector is a native function, the plugin address will be the address of the account. + /// @param selector The selector to get the configuration for. + /// @return The configuration for this selector. function getExecutionFunctionConfig(bytes4 selector) external view returns (ExecutionFunctionConfig memory); - /// @notice Gets the pre and post execution hooks for a selector - /// @param selector The selector to get the hooks for - /// @return The pre and post execution hooks for this selector + /// @notice Get the pre and post execution hooks for a selector. + /// @param selector The selector to get the hooks for. + /// @return The pre and post execution hooks for this selector. function getExecutionHooks(bytes4 selector) external view returns (ExecutionHooks[] memory); - /// @notice Gets the pre user op and runtime validation hooks associated with a selector - /// @param selector The selector to get the hooks for - /// @return preUserOpValidationHooks The pre user op validation hooks for this selector - /// @return preRuntimeValidationHooks The pre runtime validation hooks for this selector + /// @notice Get the pre user op and runtime validation hooks associated with a selector. + /// @param selector The selector to get the hooks for. + /// @return preUserOpValidationHooks The pre user op validation hooks for this selector. + /// @return preRuntimeValidationHooks The pre runtime validation hooks for this selector. function getPreValidationHooks(bytes4 selector) external view @@ -42,7 +42,7 @@ interface IAccountLoupe { FunctionReference[] memory preRuntimeValidationHooks ); - /// @notice Gets an array of all installed plugins - /// @return The addresses of all installed plugins + /// @notice Get an array of all installed plugins. + /// @return The addresses of all installed plugins. function getInstalledPlugins() external view returns (address[] memory); } diff --git a/src/interfaces/IAccountView.sol b/src/interfaces/IAccountView.sol index 8c339163..63d8ff9e 100644 --- a/src/interfaces/IAccountView.sol +++ b/src/interfaces/IAccountView.sol @@ -5,12 +5,12 @@ import {IEntryPoint} from "./erc4337/IEntryPoint.sol"; /// @title Account View Interface interface IAccountView { - /// @notice Gets the entry point for this account - /// @return entryPoint The entry point for this account + /// @notice Get the entry point for this account. + /// @return entryPoint The entry point for this account. function entryPoint() external view returns (IEntryPoint); /// @notice Get the account nonce. - /// @dev uses key 0 + /// @dev Uses key 0. /// @return nonce The next account nonce. function getNonce() external view returns (uint256); } diff --git a/src/interfaces/IPlugin.sol b/src/interfaces/IPlugin.sol index 59f57f9b..58f7de70 100644 --- a/src/interfaces/IPlugin.sol +++ b/src/interfaces/IPlugin.sol @@ -52,28 +52,9 @@ struct ManifestExternalCallPermission { bytes4[] selectors; } -/// @dev A struct describing how the plugin should be installed on a modular account. -struct PluginManifest { - // List of ERC-165 interfaceIds to add to account to support introspection checks. - bytes4[] interfaceIds; - // If this plugin depends on other plugins' validation functions, the interface IDs of - // those plugins MUST be provided here, with its position in the array matching the `dependencyIndex` - // members of `ManifestFunction` structs used in the manifest. - bytes4[] dependencyInterfaceIds; - // Execution functions defined in this plugin to be installed on the MSCA. - bytes4[] executionFunctions; - // Plugin execution functions already installed on the MSCA that this plugin will be able to call. - bytes4[] permittedExecutionSelectors; - // External addresses that this plugin will be able to call. - bool permitAnyExternalAddress; - // boolean to indicate whether the plugin needs access to spend native tokens of the account - bool canSpendNativeToken; - ManifestExternalCallPermission[] permittedExternalCalls; - ManifestAssociatedFunction[] userOpValidationFunctions; - ManifestAssociatedFunction[] runtimeValidationFunctions; - ManifestAssociatedFunction[] preUserOpValidationHooks; - ManifestAssociatedFunction[] preRuntimeValidationHooks; - ManifestExecutionHook[] executionHooks; +struct SelectorPermission { + bytes4 functionSelector; + string permissionDescription; } /// @dev A struct holding fields to describe the plugin in a purely view context. Intended for front end clients. @@ -90,9 +71,30 @@ struct PluginMetadata { SelectorPermission[] permissionDescriptors; } -struct SelectorPermission { - bytes4 functionSelector; - string permissionDescription; +/// @dev A struct describing how the plugin should be installed on a modular account. +struct PluginManifest { + // List of ERC-165 interface IDs to add to account to support introspection checks. This MUST NOT include + // IPlugin's interface ID. + bytes4[] interfaceIds; + // If this plugin depends on other plugins' validation functions, the interface IDs of those plugins MUST be + // provided here, with its position in the array matching the `dependencyIndex` members of `ManifestFunction` + // structs used in the manifest. + bytes4[] dependencyInterfaceIds; + // Execution functions defined in this plugin to be installed on the MSCA. + bytes4[] executionFunctions; + // Plugin execution functions already installed on the MSCA that this plugin will be able to call. + bytes4[] permittedExecutionSelectors; + // Boolean to indicate whether the plugin can call any external address. + bool permitAnyExternalAddress; + // Boolean to indicate whether the plugin needs access to spend native tokens of the account. If false, the + // plugin MUST still be able to spend up to the balance that it sends to the account in the same call. + bool canSpendNativeToken; + ManifestExternalCallPermission[] permittedExternalCalls; + ManifestAssociatedFunction[] userOpValidationFunctions; + ManifestAssociatedFunction[] runtimeValidationFunctions; + ManifestAssociatedFunction[] preUserOpValidationHooks; + ManifestAssociatedFunction[] preRuntimeValidationHooks; + ManifestExecutionHook[] executionHooks; } /// @title Plugin Interface diff --git a/src/interfaces/IPluginExecutor.sol b/src/interfaces/IPluginExecutor.sol index 2db6fb8e..be1f735f 100644 --- a/src/interfaces/IPluginExecutor.sol +++ b/src/interfaces/IPluginExecutor.sol @@ -3,20 +3,20 @@ pragma solidity ^0.8.22; /// @title Plugin Executor Interface interface IPluginExecutor { - /// @notice Method from calls made from plugins to other plugin execution functions. Plugins are not allowed to - /// call accounts native functions. - /// @dev Permissions must be granted to the calling plugin for the call to go through - /// @param data The call data for the call. + /// @notice Execute a call from a plugin to another plugin, via an execution function installed on the account. + /// @dev Plugins are not allowed to call native functions on the account. Permissions must be granted to the + /// calling plugin for the call to go through. + /// @param data The calldata to send to the plugin. /// @return The return data from the call. function executeFromPlugin(bytes calldata data) external payable returns (bytes memory); - /// @notice Method from calls made from plugins to external addresses. + /// @notice Execute a call from a plugin to a non-plugin address. /// @dev If the target is a plugin, the call SHOULD revert. Permissions must be granted to the calling plugin - /// for the call to go through + /// for the call to go through. /// @param target The address to be called. - /// @param value The value to pass. - /// @param data The data to pass. - /// @return The result of the call + /// @param value The value to send with the call. + /// @param data The calldata to send to the target. + /// @return The return data from the call. function executeFromPluginExternal(address target, uint256 value, bytes calldata data) external payable diff --git a/src/interfaces/IPluginManager.sol b/src/interfaces/IPluginManager.sol index c926fdb0..f5af7070 100644 --- a/src/interfaces/IPluginManager.sol +++ b/src/interfaces/IPluginManager.sol @@ -7,19 +7,19 @@ type FunctionReference is bytes21; interface IPluginManager { event PluginInstalled(address indexed plugin, bytes32 manifestHash, FunctionReference[] dependencies); event PluginUninstalled(address indexed plugin, bool indexed callbacksSucceeded); - event PluginIgnoredHookUnapplyCallbackFailure(address indexed plugin, address indexed providingPlugin); event PluginIgnoredUninstallCallbackFailure(address indexed plugin); /// @notice Install a plugin to the modular account. /// @param plugin The plugin to install. /// @param manifestHash The hash of the plugin manifest. - /// @param pluginInitData Optional data to be decoded and used by the plugin to setup initial plugin data for - /// the modular account. - /// @param dependencies The dependencies of the plugin, as described in the manifest. + /// @param pluginInstallData Optional data to be decoded and used by the plugin to setup initial plugin data + /// for the modular account. + /// @param dependencies The dependencies of the plugin, as described in the manifest. Each FunctionReference + /// MUST be composed of an installed plugin's address and a function ID of its validation function. function installPlugin( address plugin, bytes32 manifestHash, - bytes calldata pluginInitData, + bytes calldata pluginInstallData, FunctionReference[] calldata dependencies ) external; diff --git a/src/interfaces/IStandardExecutor.sol b/src/interfaces/IStandardExecutor.sol index 741e284d..710fab2c 100644 --- a/src/interfaces/IStandardExecutor.sol +++ b/src/interfaces/IStandardExecutor.sol @@ -2,11 +2,11 @@ pragma solidity ^0.8.22; struct Call { - // The target address for account to call. + // The target address for the account to call. address target; // The value sent with the call. uint256 value; - // The call data for the call. + // The calldata for the call. bytes data; } @@ -14,15 +14,15 @@ struct Call { interface IStandardExecutor { /// @notice Standard execute method. /// @dev If the target is a plugin, the call SHOULD revert. - /// @param target The target address for account to call. + /// @param target The target address for the account to call. /// @param value The value sent with the call. - /// @param data The call data for the call. + /// @param data The calldata for the call. /// @return The return data from the call. function execute(address target, uint256 value, bytes calldata data) external payable returns (bytes memory); /// @notice Standard executeBatch method. - /// @dev If the target is a plugin, the call SHOULD revert. If any of the transactions revert, the entire batch - /// reverts + /// @dev If the target is a plugin, the call SHOULD revert. If any of the calls revert, the entire batch MUST + /// revert. /// @param calls The array of calls. /// @return An array containing the return data from the calls. function executeBatch(Call[] calldata calls) external payable returns (bytes[] memory); diff --git a/test/account/AccountExecHooks.t.sol b/test/account/AccountExecHooks.t.sol index 692d2160..d3e8b409 100644 --- a/test/account/AccountExecHooks.t.sol +++ b/test/account/AccountExecHooks.t.sol @@ -431,7 +431,7 @@ contract UpgradeableModularAccountExecHooksTest is Test { account1.installPlugin({ plugin: address(mockPlugin1), manifestHash: manifestHash1, - pluginInitData: bytes(""), + pluginInstallData: bytes(""), dependencies: new FunctionReference[](0) }); @@ -486,7 +486,7 @@ contract UpgradeableModularAccountExecHooksTest is Test { account1.installPlugin({ plugin: address(mockPlugin2), manifestHash: manifestHash2, - pluginInitData: bytes(""), + pluginInstallData: bytes(""), dependencies: dependencies }); diff --git a/test/account/AccountPreValidationHooks.t.sol b/test/account/AccountPreValidationHooks.t.sol index 103c66da..afc996bd 100644 --- a/test/account/AccountPreValidationHooks.t.sol +++ b/test/account/AccountPreValidationHooks.t.sol @@ -549,7 +549,7 @@ contract UpgradeableModularAccountPreValidationHooksTest is Test { account1.installPlugin({ plugin: address(mockPlugin1), manifestHash: manifestHash1, - pluginInitData: bytes(""), + pluginInstallData: bytes(""), dependencies: new FunctionReference[](0) }); @@ -597,7 +597,7 @@ contract UpgradeableModularAccountPreValidationHooksTest is Test { account1.installPlugin({ plugin: address(mockPlugin2), manifestHash: manifestHash2, - pluginInitData: bytes(""), + pluginInstallData: bytes(""), dependencies: dependencies }); @@ -634,7 +634,7 @@ contract UpgradeableModularAccountPreValidationHooksTest is Test { account1.installPlugin({ plugin: address(mockPlugin1), manifestHash: manifestHash1, - pluginInitData: bytes(""), + pluginInstallData: bytes(""), dependencies: dependencies }); } @@ -659,7 +659,7 @@ contract UpgradeableModularAccountPreValidationHooksTest is Test { account1.installPlugin({ plugin: address(mockPlugin2), manifestHash: manifestHash2, - pluginInitData: bytes(""), + pluginInstallData: bytes(""), dependencies: dependencies }); } diff --git a/test/account/AccountReturnData.t.sol b/test/account/AccountReturnData.t.sol index 4467663e..27cad574 100644 --- a/test/account/AccountReturnData.t.sol +++ b/test/account/AccountReturnData.t.sol @@ -73,7 +73,7 @@ contract AccountReturnDataTest is Test { account.installPlugin({ plugin: address(resultCreatorPlugin), manifestHash: resultCreatorManifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); // Add the result consumer plugin to the account @@ -81,7 +81,7 @@ contract AccountReturnDataTest is Test { account.installPlugin({ plugin: address(resultConsumerPlugin), manifestHash: resultConsumerManifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); } diff --git a/test/account/ExecuteFromPluginPermissions.t.sol b/test/account/ExecuteFromPluginPermissions.t.sol index be9a67be..6d931b5b 100644 --- a/test/account/ExecuteFromPluginPermissions.t.sol +++ b/test/account/ExecuteFromPluginPermissions.t.sol @@ -89,7 +89,7 @@ contract ExecuteFromPluginPermissionsTest is Test { account.installPlugin({ plugin: address(resultCreatorPlugin), manifestHash: resultCreatorManifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); // Add the EFP caller plugin to the account @@ -97,7 +97,7 @@ contract ExecuteFromPluginPermissionsTest is Test { account.installPlugin({ plugin: address(efpCallerPlugin), manifestHash: efpCallerManifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); @@ -107,7 +107,7 @@ contract ExecuteFromPluginPermissionsTest is Test { account.installPlugin({ plugin: address(efpCallerPluginAnyExternal), manifestHash: efpCallerAnyExternalManifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); @@ -117,7 +117,7 @@ contract ExecuteFromPluginPermissionsTest is Test { account.installPlugin({ plugin: address(efpCallerPluginAnyExternalCanSpendNativeToken), manifestHash: efpCallerAnyExternalCanSpendNativeTokenManifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); @@ -126,7 +126,7 @@ contract ExecuteFromPluginPermissionsTest is Test { account.installPlugin({ plugin: address(efpExecutionHookPlugin), manifestHash: efpExecutionHookPluginManifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); } diff --git a/test/account/ManifestValidity.t.sol b/test/account/ManifestValidity.t.sol index 1ea54f17..83f7dc5b 100644 --- a/test/account/ManifestValidity.t.sol +++ b/test/account/ManifestValidity.t.sol @@ -76,7 +76,7 @@ contract ManifestValidityTest is Test { account.installPlugin({ plugin: address(plugin), manifestHash: manifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); } @@ -93,7 +93,7 @@ contract ManifestValidityTest is Test { account.installPlugin({ plugin: address(plugin), manifestHash: manifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); } @@ -110,7 +110,7 @@ contract ManifestValidityTest is Test { account.installPlugin({ plugin: address(plugin), manifestHash: manifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); } @@ -125,7 +125,7 @@ contract ManifestValidityTest is Test { account.installPlugin({ plugin: address(plugin), manifestHash: manifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); } @@ -140,7 +140,7 @@ contract ManifestValidityTest is Test { account.installPlugin({ plugin: address(plugin), manifestHash: manifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); } @@ -156,7 +156,7 @@ contract ManifestValidityTest is Test { account.installPlugin({ plugin: address(plugin), manifestHash: manifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); } @@ -172,7 +172,7 @@ contract ManifestValidityTest is Test { account.installPlugin({ plugin: address(plugin), manifestHash: manifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); } @@ -187,7 +187,7 @@ contract ManifestValidityTest is Test { account.installPlugin({ plugin: address(plugin), manifestHash: manifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); } diff --git a/test/account/UpgradeableModularAccount.t.sol b/test/account/UpgradeableModularAccount.t.sol index 37f64085..3fc3dec3 100644 --- a/test/account/UpgradeableModularAccount.t.sol +++ b/test/account/UpgradeableModularAccount.t.sol @@ -489,7 +489,7 @@ contract UpgradeableModularAccountTest is Test { IPluginManager(account2).installPlugin({ plugin: address(plugin), manifestHash: manifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); vm.stopPrank(); diff --git a/test/account/UpgradeableModularAccountPluginManager.t.sol b/test/account/UpgradeableModularAccountPluginManager.t.sol index a5e24733..ee436ca3 100644 --- a/test/account/UpgradeableModularAccountPluginManager.t.sol +++ b/test/account/UpgradeableModularAccountPluginManager.t.sol @@ -77,7 +77,6 @@ contract UpgradeableModularAccountPluginManagerTest is Test { event PluginInstalled(address indexed plugin, bytes32 manifestHash, FunctionReference[] dependencies); event PluginUninstalled(address indexed plugin, bool indexed callbacksSucceeded); - event PluginIgnoredHookUnapplyCallbackFailure(address indexed plugin, address indexed providingPlugin); event PluginIgnoredUninstallCallbackFailure(address indexed plugin); event ReceivedCall(bytes msgData, uint256 msgValue); @@ -138,7 +137,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { IPluginManager(account2).installPlugin({ plugin: address(sessionKeyPlugin), manifestHash: manifestHash, - pluginInitData: abi.encode( + pluginInstallData: abi.encode( sessionKeys, new bytes32[](sessionKeys.length), new bytes[][](sessionKeys.length) ), dependencies: dependencies @@ -150,7 +149,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { IPluginManager(account2).installPlugin({ plugin: address(tokenReceiverPlugin), manifestHash: manifestHash, - pluginInitData: abi.encode(uint48(1 days)), + pluginInstallData: abi.encode(uint48(1 days)), dependencies: new FunctionReference[](0) }); @@ -176,7 +175,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { IPluginManager(account2).installPlugin({ plugin: address(mockPluginWithBadPermittedExec), manifestHash: manifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); } @@ -188,7 +187,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { IPluginManager(account2).installPlugin({ plugin: address(tokenReceiverPlugin), manifestHash: bytes32(0), - pluginInitData: abi.encode(uint48(1 days)), + pluginInstallData: abi.encode(uint48(1 days)), dependencies: new FunctionReference[](0) }); } @@ -203,7 +202,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { IPluginManager(account2).installPlugin({ plugin: address(badPlugin), manifestHash: bytes32(0), - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); } @@ -215,7 +214,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { IPluginManager(account2).installPlugin({ plugin: address(tokenReceiverPlugin), manifestHash: manifestHash, - pluginInitData: abi.encode(uint48(1 days)), + pluginInstallData: abi.encode(uint48(1 days)), dependencies: new FunctionReference[](0) }); @@ -227,7 +226,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { IPluginManager(account2).installPlugin({ plugin: address(tokenReceiverPlugin), manifestHash: manifestHash, - pluginInitData: abi.encode(uint48(1 days)), + pluginInstallData: abi.encode(uint48(1 days)), dependencies: new FunctionReference[](0) }); } @@ -249,7 +248,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { IPluginManager(account2).installPlugin({ plugin: address(mockPluginBad), manifestHash: manifestHashBad, - pluginInitData: bytes(""), + pluginInstallData: bytes(""), dependencies: new FunctionReference[](0) }); } @@ -271,7 +270,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { IPluginManager(account2).installPlugin({ plugin: address(mockPluginBad), manifestHash: manifestHashBad, - pluginInitData: bytes(""), + pluginInstallData: bytes(""), dependencies: new FunctionReference[](0) }); } @@ -289,7 +288,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { IPluginManager(account2).installPlugin({ plugin: address(mockPluginBad), manifestHash: manifestHashBad, - pluginInitData: bytes(""), + pluginInstallData: bytes(""), dependencies: new FunctionReference[](0) }); } @@ -312,7 +311,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { IPluginManager(account2).installPlugin({ plugin: address(mockPluginBad), manifestHash: manifestHashBad, - pluginInitData: bytes(""), + pluginInstallData: bytes(""), dependencies: new FunctionReference[](0) }); } @@ -334,7 +333,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { IPluginManager(account2).installPlugin({ plugin: address(newPlugin), manifestHash: manifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: dependencies }); @@ -348,7 +347,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { IPluginManager(account2).installPlugin({ plugin: address(newPlugin), manifestHash: manifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: dependencies }); } @@ -361,7 +360,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { IPluginManager(account2).installPlugin({ plugin: address(plugin), manifestHash: manifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); @@ -382,7 +381,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { IPluginManager(account2).installPlugin({ plugin: address(plugin), manifestHash: manifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); @@ -410,7 +409,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { IPluginManager(account2).installPlugin({ plugin: address(plugin), manifestHash: manifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); @@ -455,7 +454,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { IPluginManager(account2).installPlugin({ plugin: address(plugin), manifestHash: manifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); @@ -648,7 +647,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { account2.installPlugin({ plugin: address(plugin), manifestHash: manifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); } @@ -664,7 +663,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { IPluginManager(account2).installPlugin({ plugin: address(plugin), manifestHash: manifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); @@ -679,7 +678,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { IPluginManager(account2).installPlugin({ plugin: address(plugin), manifestHash: manifestHash, - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); return address(plugin); @@ -695,14 +694,14 @@ contract UpgradeableModularAccountPluginManagerTest is Test { IPluginManager(account2).installPlugin({ plugin: address(hooksPlugin), manifestHash: keccak256(abi.encode(hooksPlugin.pluginManifest())), - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); MockPlugin plugin = new MockPlugin(manifest); IPluginManager(account2).installPlugin({ plugin: address(plugin), manifestHash: keccak256(abi.encode(plugin.pluginManifest())), - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); return (address(plugin), address(hooksPlugin)); diff --git a/test/account/ValidationIntersection.t.sol b/test/account/ValidationIntersection.t.sol index 6a71577f..adf99b83 100644 --- a/test/account/ValidationIntersection.t.sol +++ b/test/account/ValidationIntersection.t.sol @@ -73,19 +73,19 @@ contract ValidationIntersectionTest is Test { account1.installPlugin({ plugin: address(noHookPlugin), manifestHash: keccak256(abi.encode(noHookPlugin.pluginManifest())), - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); account1.installPlugin({ plugin: address(oneHookPlugin), manifestHash: keccak256(abi.encode(oneHookPlugin.pluginManifest())), - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); account1.installPlugin({ plugin: address(twoHookPlugin), manifestHash: keccak256(abi.encode(twoHookPlugin.pluginManifest())), - pluginInitData: "", + pluginInstallData: "", dependencies: new FunctionReference[](0) }); vm.stopPrank(); diff --git a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol index e67fbbad..b12d8812 100644 --- a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol +++ b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol @@ -99,7 +99,7 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { account1.installPlugin({ plugin: address(sessionKeyPlugin), manifestHash: manifestHash, - pluginInitData: abi.encode(new address[](0), new bytes32[](0), new bytes[][](0)), + pluginInstallData: abi.encode(new address[](0), new bytes32[](0), new bytes[][](0)), dependencies: dependencies }); } @@ -208,7 +208,7 @@ contract SessionKeyPluginWithMultiOwnerTest is Test { account1.installPlugin({ plugin: address(sessionKeyPlugin), manifestHash: manifestHash, - pluginInitData: onInstallData, + pluginInstallData: onInstallData, dependencies: dependencies }); diff --git a/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol b/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol index 25132aa0..32d9b911 100644 --- a/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol @@ -101,7 +101,7 @@ contract SessionKeyERC20SpendLimitsTest is Test { account1.installPlugin({ plugin: address(sessionKeyPlugin), manifestHash: manifestHash, - pluginInitData: abi.encode(new address[](0), new bytes32[](0), new bytes[][](0)), + pluginInstallData: abi.encode(new address[](0), new bytes32[](0), new bytes[][](0)), dependencies: dependencies }); diff --git a/test/plugin/session/permissions/SessionKeyGasLimits.t.sol b/test/plugin/session/permissions/SessionKeyGasLimits.t.sol index 3ab483f4..7c262f1f 100644 --- a/test/plugin/session/permissions/SessionKeyGasLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyGasLimits.t.sol @@ -92,7 +92,7 @@ contract SessionKeyGasLimitsTest is Test { account1.installPlugin({ plugin: address(sessionKeyPlugin), manifestHash: manifestHash, - pluginInitData: abi.encode(new address[](0), new bytes32[](0), new bytes[][](0)), + pluginInstallData: abi.encode(new address[](0), new bytes32[](0), new bytes[][](0)), dependencies: dependencies }); diff --git a/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol b/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol index 5596fa47..682bdcef 100644 --- a/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol @@ -97,7 +97,7 @@ contract SessionKeyNativeTokenSpendLimitsTest is Test { account1.installPlugin({ plugin: address(sessionKeyPlugin), manifestHash: manifestHash, - pluginInitData: abi.encode(new address[](0), new bytes32[](0), new bytes[][](0)), + pluginInstallData: abi.encode(new address[](0), new bytes32[](0), new bytes[][](0)), dependencies: dependencies }); diff --git a/test/plugin/session/permissions/SessionKeyPermissions.t.sol b/test/plugin/session/permissions/SessionKeyPermissions.t.sol index 7b17b93d..7b8315b0 100644 --- a/test/plugin/session/permissions/SessionKeyPermissions.t.sol +++ b/test/plugin/session/permissions/SessionKeyPermissions.t.sol @@ -107,7 +107,7 @@ contract SessionKeyPermissionsTest is Test { account1.installPlugin({ plugin: address(sessionKeyPlugin), manifestHash: manifestHash, - pluginInitData: abi.encode(new address[](0), new bytes32[](0), new bytes[][](0)), + pluginInstallData: abi.encode(new address[](0), new bytes32[](0), new bytes[][](0)), dependencies: dependencies }); @@ -644,7 +644,7 @@ contract SessionKeyPermissionsTest is Test { account1.installPlugin({ plugin: address(sessionKeyPlugin), manifestHash: keccak256(abi.encode(sessionKeyPlugin.pluginManifest())), - pluginInitData: abi.encode(new address[](0), new bytes32[](0), new bytes[][](0)), + pluginInstallData: abi.encode(new address[](0), new bytes32[](0), new bytes[][](0)), dependencies: dependencies }); vm.stopPrank(); @@ -685,7 +685,7 @@ contract SessionKeyPermissionsTest is Test { account1.installPlugin({ plugin: address(sessionKeyPlugin), manifestHash: manifestHash, - pluginInitData: abi.encode(sessionKeys, tags, sessionKeyPermissions), + pluginInstallData: abi.encode(sessionKeys, tags, sessionKeyPermissions), dependencies: dependencies }); } From 849f1ac993afaa4b218b247be34da6cdef92febf Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Tue, 23 Jan 2024 03:30:37 -0500 Subject: [PATCH 098/106] fix: remove PluginIgnoredUninstallCallbackFailure event (#111) --- src/account/PluginManagerInternals.sol | 11 +++++------ src/account/UpgradeableModularAccount.sol | 2 +- src/interfaces/IPluginManager.sol | 3 +-- test/account/AccountExecHooks.t.sol | 2 +- test/account/AccountPreValidationHooks.t.sol | 2 +- .../UpgradeableModularAccountPluginManager.t.sol | 5 +---- test/account/phases/AccountStatePhases.t.sol | 2 +- 7 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/account/PluginManagerInternals.sol b/src/account/PluginManagerInternals.sol index 56efbf29..580613d1 100644 --- a/src/account/PluginManagerInternals.sol +++ b/src/account/PluginManagerInternals.sol @@ -477,7 +477,7 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { emit PluginInstalled(plugin, manifestHash, dependencies); } - function _uninstallPlugin(UninstallPluginArgs memory args, bytes calldata uninstallData) internal { + function _uninstallPlugin(UninstallPluginArgs memory args, bytes calldata pluginUninstallData) internal { AccountStorage storage storage_ = _getAccountStorage(); // Check if the plugin exists. @@ -628,18 +628,17 @@ abstract contract PluginManagerInternals is IPluginManager, AccountStorageV1 { } // Clear the plugin storage for the account. - bool callbacksSucceeded = true; + bool onUninstallSucceeded = true; // solhint-disable-next-line no-empty-blocks - try IPlugin(args.plugin).onUninstall{gas: args.callbackGasLimit}(uninstallData) {} + try IPlugin(args.plugin).onUninstall{gas: args.callbackGasLimit}(pluginUninstallData) {} catch (bytes memory revertReason) { if (!args.forceUninstall) { revert PluginUninstallCallbackFailed(args.plugin, revertReason); } - callbacksSucceeded = false; - emit PluginIgnoredUninstallCallbackFailure(args.plugin); + onUninstallSucceeded = false; } - emit PluginUninstalled(args.plugin, callbacksSucceeded); + emit PluginUninstalled(args.plugin, onUninstallSucceeded); } function _isValidPluginManifest(PluginManifest memory manifest, bytes32 manifestHash) diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index fdd030e9..3f8f6a8d 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -67,7 +67,7 @@ contract UpgradeableModularAccount is // plugin manifest has changed. If empty, uses the default behavior of // calling the plugin to get its current manifest. bytes serializedManifest; - // If true, will complete the uninstall even if `onUninstall` callbacks revert. Available as an escape + // If true, will complete the uninstall even if the `onUninstall` callback reverts. Available as an escape // hatch if a plugin is blocking uninstall. bool forceUninstall; // Maximum amount of gas allowed for each uninstall callback function diff --git a/src/interfaces/IPluginManager.sol b/src/interfaces/IPluginManager.sol index f5af7070..a04c3253 100644 --- a/src/interfaces/IPluginManager.sol +++ b/src/interfaces/IPluginManager.sol @@ -6,8 +6,7 @@ type FunctionReference is bytes21; /// @title Plugin Manager Interface interface IPluginManager { event PluginInstalled(address indexed plugin, bytes32 manifestHash, FunctionReference[] dependencies); - event PluginUninstalled(address indexed plugin, bool indexed callbacksSucceeded); - event PluginIgnoredUninstallCallbackFailure(address indexed plugin); + event PluginUninstalled(address indexed plugin, bool indexed onUninstallSucceeded); /// @notice Install a plugin to the modular account. /// @param plugin The plugin to install. diff --git a/test/account/AccountExecHooks.t.sol b/test/account/AccountExecHooks.t.sol index d3e8b409..568d737e 100644 --- a/test/account/AccountExecHooks.t.sol +++ b/test/account/AccountExecHooks.t.sol @@ -64,7 +64,7 @@ contract UpgradeableModularAccountExecHooksTest is Test { PluginManifest public m2; event PluginInstalled(address indexed plugin, bytes32 manifestHash, FunctionReference[] dependencies); - event PluginUninstalled(address indexed plugin, bool indexed callbacksSucceeded); + event PluginUninstalled(address indexed plugin, bool indexed onUninstallSucceeded); function setUp() public { entryPoint = IEntryPoint(address(new EntryPoint())); diff --git a/test/account/AccountPreValidationHooks.t.sol b/test/account/AccountPreValidationHooks.t.sol index afc996bd..62a6decd 100644 --- a/test/account/AccountPreValidationHooks.t.sol +++ b/test/account/AccountPreValidationHooks.t.sol @@ -65,7 +65,7 @@ contract UpgradeableModularAccountPreValidationHooksTest is Test { uint256 public constant VERIFICATION_GAS_LIMIT = 1000000; event PluginInstalled(address indexed plugin, bytes32 manifestHash, FunctionReference[] dependencies); - event PluginUninstalled(address indexed plugin, bool indexed callbacksSucceeded); + event PluginUninstalled(address indexed plugin, bool indexed onUninstallSucceeded); function setUp() public { entryPoint = IEntryPoint(address(new EntryPoint())); diff --git a/test/account/UpgradeableModularAccountPluginManager.t.sol b/test/account/UpgradeableModularAccountPluginManager.t.sol index ee436ca3..afd579a5 100644 --- a/test/account/UpgradeableModularAccountPluginManager.t.sol +++ b/test/account/UpgradeableModularAccountPluginManager.t.sol @@ -76,8 +76,7 @@ contract UpgradeableModularAccountPluginManagerTest is Test { uint256 public constant VERIFICATION_GAS_LIMIT = 2000000; event PluginInstalled(address indexed plugin, bytes32 manifestHash, FunctionReference[] dependencies); - event PluginUninstalled(address indexed plugin, bool indexed callbacksSucceeded); - event PluginIgnoredUninstallCallbackFailure(address indexed plugin); + event PluginUninstalled(address indexed plugin, bool indexed onUninstallSucceeded); event ReceivedCall(bytes msgData, uint256 msgValue); function setUp() public { @@ -554,8 +553,6 @@ contract UpgradeableModularAccountPluginManagerTest is Test { }) ); vm.expectEmit(true, true, true, true); - emit PluginIgnoredUninstallCallbackFailure(plugin); - vm.expectEmit(true, true, true, true); emit PluginUninstalled(plugin, false); IPluginManager(account2).uninstallPlugin{gas: 100_000}({ plugin: plugin, diff --git a/test/account/phases/AccountStatePhases.t.sol b/test/account/phases/AccountStatePhases.t.sol index 0abc9e63..89cfbacf 100644 --- a/test/account/phases/AccountStatePhases.t.sol +++ b/test/account/phases/AccountStatePhases.t.sol @@ -85,7 +85,7 @@ contract AccountStatePhasesTest is Test { // Event re-declarations for vm.expectEmit event PluginInstalled(address indexed plugin, bytes32 manifestHash, FunctionReference[] dependencies); - event PluginUninstalled(address indexed plugin, bool indexed callbacksSucceeded); + event PluginUninstalled(address indexed plugin, bool indexed onUninstallSucceeded); event ReceivedCall(bytes msgData, uint256 msgValue); // Empty arrays for convenience From 7b2393213fb644747f1e2346af9ddb51de006854 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Tue, 23 Jan 2024 12:05:02 -0500 Subject: [PATCH 099/106] fix: obsolete comment within _doPreExecHooks (#112) --- src/account/UpgradeableModularAccount.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index 3f8f6a8d..5a3c0f45 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -587,8 +587,8 @@ contract UpgradeableModularAccount is if (hasPostOnlyExecHooks) { // If we have post-only hooks, we allocate an single FunctionReference[] for them, and one element - // in the args for their empty `bytes` argument. We put this into the first element of the post - // hooks in order to have it run last. + // in the args for their empty `bytes` argument. We put this into the last element of the post + // hooks, which means post-only hooks will run before any other post hooks. postHooksToRun[postHooksToRunLength - 1] = CastLib.toFunctionReferenceArray(selectorData.executionHooks.postOnlyHooks.getAll()); } From 80c8803a02b64856008869f76c9d96693cfb0209 Mon Sep 17 00:00:00 2001 From: howy <132113803+howydev@users.noreply.github.com> Date: Tue, 23 Jan 2024 13:42:54 -0500 Subject: [PATCH 100/106] style: removed unused const (#113) --- src/plugins/session/SessionKeyPlugin.sol | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/plugins/session/SessionKeyPlugin.sol b/src/plugins/session/SessionKeyPlugin.sol index 7960b3f8..c35239fd 100644 --- a/src/plugins/session/SessionKeyPlugin.sol +++ b/src/plugins/session/SessionKeyPlugin.sol @@ -35,9 +35,7 @@ import {Call, IStandardExecutor} from "../../interfaces/IStandardExecutor.sol"; import { AssociatedLinkedListSet, AssociatedLinkedListSetLib } from "../../libraries/AssociatedLinkedListSetLib.sol"; -import { - SetValue, SENTINEL_VALUE, SIG_VALIDATION_PASSED, SIG_VALIDATION_FAILED -} from "../../libraries/Constants.sol"; +import {SetValue, SENTINEL_VALUE, SIG_VALIDATION_FAILED} from "../../libraries/Constants.sol"; import {BasePlugin} from "../BasePlugin.sol"; import {ISessionKeyPlugin} from "./ISessionKeyPlugin.sol"; import {SessionKeyPermissions} from "./permissions/SessionKeyPermissions.sol"; From 4199f1069956618f516a368b907d41811ce4f1f1 Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Tue, 23 Jan 2024 11:39:18 -0800 Subject: [PATCH 101/106] fix: Cleanup comments, unused vars, config files. (#114) --- .solhint-src.json | 2 +- .solhint-test.json | 2 +- script/Deploy.s.sol | 4 ++-- slither.config.json | 2 +- src/account/AccountExecutor.sol | 4 ++-- src/account/AccountLoupe.sol | 4 ++-- src/account/AccountStorageInitializable.sol | 4 ++-- src/account/AccountStorageV1.sol | 12 ++++++------ src/account/PluginManagerInternals.sol | 4 ++-- src/account/UpgradeableModularAccount.sol | 5 ++--- src/factory/MultiOwnerMSCAFactory.sol | 4 ++-- src/factory/MultiOwnerTokenReceiverMSCAFactory.sol | 4 ++-- src/helpers/CastLib.sol | 4 ++-- src/helpers/FactoryHelpers.sol | 4 ++-- src/helpers/FunctionReferenceLib.sol | 4 ++-- src/helpers/KnownSelectors.sol | 4 ++-- src/helpers/ValidationDataHelpers.sol | 4 ++-- src/interfaces/IAccountInitializable.sol | 2 +- src/libraries/AssociatedLinkedListSetLib.sol | 2 +- src/libraries/LinkedListSetLib.sol | 4 ++-- src/plugins/BasePlugin.sol | 4 ++-- src/plugins/TokenReceiverPlugin.sol | 4 ++-- src/plugins/owner/IMultiOwnerPlugin.sol | 4 ++-- src/plugins/owner/MultiOwnerPlugin.sol | 4 ++-- src/plugins/session/ISessionKeyPlugin.sol | 5 ++--- src/plugins/session/SessionKeyPlugin.sol | 4 ++-- .../permissions/ISessionKeyPermissionsUpdates.sol | 4 ++-- .../session/permissions/SessionKeyPermissions.sol | 4 ++-- .../permissions/SessionKeyPermissionsBase.sol | 4 ++-- .../permissions/SessionKeyPermissionsLoupe.sol | 4 ++-- test/TestUtils.sol | 4 ++-- test/Utils.sol | 4 ++-- test/account/AccountExecHooks.t.sol | 4 ++-- test/account/AccountLoupe.t.sol | 4 ++-- test/account/AccountPreValidationHooks.t.sol | 4 ++-- test/account/AccountReturnData.t.sol | 4 ++-- test/account/AccountStorage.t.sol | 4 ++-- test/account/ExecuteFromPluginPermissions.t.sol | 4 ++-- test/account/ManifestValidity.t.sol | 4 ++-- test/account/UpgradeableModularAccount.t.sol | 4 ++-- .../UpgradeableModularAccountPluginManager.t.sol | 4 ++-- test/account/ValidationIntersection.t.sol | 4 ++-- test/account/phases/AccountStatePhases.t.sol | 4 ++-- test/account/phases/AccountStatePhasesExec.t.sol | 4 ++-- .../phases/AccountStatePhasesRTValidation.t.sol | 4 ++-- .../phases/AccountStatePhasesUOValidation.t.sol | 4 ++-- test/comparison/CompareSimpleAccount.t.sol | 4 ++-- test/factory/MultiOwnerMSCAFactoryTest.t.sol | 4 ++-- .../factory/MultiOwnerTokenReceiverFactoryTest.t.sol | 4 ++-- test/helpers/FunctionReferenceLib.t.sol | 4 ++-- test/helpers/KnownSelectors.t.sol | 4 ++-- .../AssociatedLinkedListSetLibInvariants.t.sol | 4 ++-- test/invariant/LLSLRepro.t.sol | 4 ++-- test/invariant/LinkedListSetLibInvariants.t.sol | 4 ++-- .../handlers/AssociatedLinkedListSetHandler.sol | 4 ++-- test/invariant/handlers/LinkedListSetHandler.sol | 4 ++-- test/libraries/AssociatedLinkedListSetLib.t.sol | 4 ++-- test/libraries/CountableLinkedListSetLib.t.sol | 4 ++-- test/libraries/LinkedListSetLib.t.sol | 4 ++-- test/libraries/PluginStorageLib.t.sol | 4 ++-- test/mocks/ContractOwner.sol | 4 ++-- test/mocks/Counter.sol | 4 ++-- test/mocks/Counter.t.sol | 4 ++-- test/mocks/MockDiamondStorageContract.sol | 4 ++-- test/mocks/MockPlugin.sol | 4 ++-- test/mocks/plugins/AccountStateMutatingPlugin.sol | 4 ++-- test/mocks/plugins/BadTransferOwnershipPlugin.sol | 4 ++-- test/mocks/plugins/BaseTestPlugin.sol | 4 ++-- test/mocks/plugins/ChangingManifestPlugin.sol | 4 ++-- test/mocks/plugins/ComprehensivePlugin.sol | 4 ++-- .../mocks/plugins/ExecFromPluginPermissionsMocks.sol | 4 ++-- test/mocks/plugins/ManifestValidityMocks.sol | 4 ++-- test/mocks/plugins/ReturnDataPluginMocks.sol | 4 ++-- test/mocks/plugins/UninstallErrorsPlugin.sol | 4 ++-- test/mocks/plugins/ValidationPluginMocks.sol | 4 ++-- test/mocks/tokens/MockERC1155.sol | 4 ++-- test/mocks/tokens/MockERC20.sol | 4 ++-- test/mocks/tokens/MockERC777.sol | 4 ++-- test/plugin/TokenReceiverPlugin.t.sol | 4 ++-- test/plugin/owner/MultiOwnerPlugin.t.sol | 4 ++-- test/plugin/owner/MultiOwnerPluginIntegration.t.sol | 4 ++-- .../session/SessionKeyPluginWithMultiOwner.t.sol | 4 ++-- .../permissions/SessionKeyERC20SpendLimits.t.sol | 4 ++-- .../session/permissions/SessionKeyGasLimits.t.sol | 4 ++-- .../SessionKeyNativeTokenSpendLimits.t.sol | 4 ++-- .../session/permissions/SessionKeyPermissions.t.sol | 4 ++-- test/upgrade/LightAccountToMSCA.t.sol | 4 ++-- test/upgrade/MSCAToMSCA.t.sol | 4 ++-- 88 files changed, 175 insertions(+), 177 deletions(-) diff --git a/.solhint-src.json b/.solhint-src.json index 5245a9b7..2e61e549 100644 --- a/.solhint-src.json +++ b/.solhint-src.json @@ -3,7 +3,7 @@ "rules": { "immutable-vars-naming": ["error"], "no-unused-import": ["error"], - "compiler-version": ["error", ">=0.8.21"], + "compiler-version": ["error", ">=0.8.22"], "func-visibility": ["error", { "ignoreConstructors": true }], "max-line-length": ["error", 120], "func-param-name-mixedcase": ["error"], diff --git a/.solhint-test.json b/.solhint-test.json index 69a6a552..8fbdf703 100644 --- a/.solhint-test.json +++ b/.solhint-test.json @@ -4,7 +4,7 @@ "func-name-mixedcase": "off", "immutable-vars-naming": ["error"], "no-unused-import": ["error"], - "compiler-version": ["error", ">=0.8.21"], + "compiler-version": ["error", ">=0.8.22"], "func-visibility": ["error", { "ignoreConstructors": true }], "max-line-length": ["error", 120], "max-states-count": ["warn", 30], diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index 18e6dcb3..b1c3cb96 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/slither.config.json b/slither.config.json index fd69b55d..06744d90 100644 --- a/slither.config.json +++ b/slither.config.json @@ -1,3 +1,3 @@ { - "filter_paths": "lib" + "filter_paths": "(lib/|test/)" } \ No newline at end of file diff --git a/src/account/AccountExecutor.sol b/src/account/AccountExecutor.sol index ace020c2..f835ca99 100644 --- a/src/account/AccountExecutor.sol +++ b/src/account/AccountExecutor.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/src/account/AccountLoupe.sol b/src/account/AccountLoupe.sol index 0e6b3a51..3bcfbe2a 100644 --- a/src/account/AccountLoupe.sol +++ b/src/account/AccountLoupe.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/src/account/AccountStorageInitializable.sol b/src/account/AccountStorageInitializable.sol index 23eb67ca..b0370358 100644 --- a/src/account/AccountStorageInitializable.sol +++ b/src/account/AccountStorageInitializable.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/src/account/AccountStorageV1.sol b/src/account/AccountStorageV1.sol index f6c38f8b..526c1b50 100644 --- a/src/account/AccountStorageV1.sol +++ b/src/account/AccountStorageV1.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; @@ -40,15 +40,15 @@ contract AccountStorageV1 { mapping(bytes24 => bool) callPermitted; // keys = address(calling plugin), target address mapping(IPlugin => mapping(address => PermittedExternalCallData)) permittedExternalCalls; - // For ERC165 introspection, each count indicates support from account or an installed plugin - // 0 indicate the account does not support the interface and all plugins that support this interface have - // been uninstalled + // For ERC165 introspection, each count indicates support from account or an installed plugin. + // 0 indicates the account does not support the interface and all plugins that support this interface have + // been uninstalled. mapping(bytes4 => uint256) supportedInterfaces; } struct PluginData { bool anyExternalAddressPermitted; - // boolean to indicate if the plugin can spend native tokens, if any of the execution function can spend + // A boolean to indicate if the plugin can spend native tokens, if any of the execution function can spend // native tokens, a plugin is considered to be able to spend native tokens of the accounts bool canSpendNativeToken; bytes32 manifestHash; diff --git a/src/account/PluginManagerInternals.sol b/src/account/PluginManagerInternals.sol index 580613d1..751567eb 100644 --- a/src/account/PluginManagerInternals.sol +++ b/src/account/PluginManagerInternals.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index 5a3c0f45..e51cb0fd 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; @@ -86,7 +86,6 @@ contract UpgradeableModularAccount is event ModularAccountInitialized(IEntryPoint indexed entryPoint); error AlwaysDenyRule(); - error AuthorizeUpgradeReverted(bytes revertReason); error ExecFromPluginNotPermitted(address plugin, bytes4 selector); error ExecFromPluginExternalNotPermitted(address plugin, address target, uint256 value, bytes data); error NativeTokenSpendingNotPermitted(address plugin); diff --git a/src/factory/MultiOwnerMSCAFactory.sol b/src/factory/MultiOwnerMSCAFactory.sol index b1a2e368..36c70c66 100644 --- a/src/factory/MultiOwnerMSCAFactory.sol +++ b/src/factory/MultiOwnerMSCAFactory.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol b/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol index 44925079..8507c6c3 100644 --- a/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol +++ b/src/factory/MultiOwnerTokenReceiverMSCAFactory.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/src/helpers/CastLib.sol b/src/helpers/CastLib.sol index 04e32450..9e7e466c 100644 --- a/src/helpers/CastLib.sol +++ b/src/helpers/CastLib.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/src/helpers/FactoryHelpers.sol b/src/helpers/FactoryHelpers.sol index cdbaab9b..997371e2 100644 --- a/src/helpers/FactoryHelpers.sol +++ b/src/helpers/FactoryHelpers.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/src/helpers/FunctionReferenceLib.sol b/src/helpers/FunctionReferenceLib.sol index e77e3a09..bf4bd74d 100644 --- a/src/helpers/FunctionReferenceLib.sol +++ b/src/helpers/FunctionReferenceLib.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/src/helpers/KnownSelectors.sol b/src/helpers/KnownSelectors.sol index 77e48052..2c8cb77a 100644 --- a/src/helpers/KnownSelectors.sol +++ b/src/helpers/KnownSelectors.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/src/helpers/ValidationDataHelpers.sol b/src/helpers/ValidationDataHelpers.sol index 7cf75da9..4a7a5ba2 100644 --- a/src/helpers/ValidationDataHelpers.sol +++ b/src/helpers/ValidationDataHelpers.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/src/interfaces/IAccountInitializable.sol b/src/interfaces/IAccountInitializable.sol index 75236bdd..8fd0f2c7 100644 --- a/src/interfaces/IAccountInitializable.sol +++ b/src/interfaces/IAccountInitializable.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.22; /// @title Account Initializable Interface interface IAccountInitializable { /// @notice Initialize the account with a set of plugins. - /// @dev No dependencies or hooks can be injected with this installation. + /// @dev No dependencies may be provided with this installation. /// @param plugins The plugins to install. /// @param pluginInitData The plugin init data for each plugin. function initialize(address[] calldata plugins, bytes calldata pluginInitData) external; diff --git a/src/libraries/AssociatedLinkedListSetLib.sol b/src/libraries/AssociatedLinkedListSetLib.sol index 963490b3..bcf6d6c0 100644 --- a/src/libraries/AssociatedLinkedListSetLib.sol +++ b/src/libraries/AssociatedLinkedListSetLib.sol @@ -466,7 +466,7 @@ library AssociatedLinkedListSetLib { // This segment is memory-safe because it only uses the scratch space memory after the value of the free // memory pointer. - // See https://docs.soliditylang.org/en/v0.8.21/assembly.html#memory-safety + // See https://docs.soliditylang.org/en/v0.8.22/assembly.html#memory-safety assembly ("memory-safe") { // Clean upper bits of arguments associated := and(associated, 0xffffffffffffffffffffffffffffffffffffffff) diff --git a/src/libraries/LinkedListSetLib.sol b/src/libraries/LinkedListSetLib.sol index 6b3b68cc..de42dbbc 100644 --- a/src/libraries/LinkedListSetLib.sol +++ b/src/libraries/LinkedListSetLib.sol @@ -263,11 +263,11 @@ library LinkedListSetLib { // This is accomplished by first writing to memory after the free memory pointer, // then updating the free memory pointer to cover the newly-allocated data. // To the compiler, writes to memory after the free memory pointer are considered "memory safe". - // See https://docs.soliditylang.org/en/v0.8.21/assembly.html#memory-safety + // See https://docs.soliditylang.org/en/v0.8.22/assembly.html#memory-safety // Stack variable lifting done when compiling with via-ir will only ever place variables into memory // locations // below the current free memory pointer, so it is safe to compile this library with via-ir. - // See https://docs.soliditylang.org/en/v0.8.21/yul.html#memoryguard + // See https://docs.soliditylang.org/en/v0.8.22/yul.html#memoryguard assembly ("memory-safe") { // It is critical that no other memory allocations occur between: // - loading the value of the free memory pointer into `ret` diff --git a/src/plugins/BasePlugin.sol b/src/plugins/BasePlugin.sol index c9239819..2d4b44c3 100644 --- a/src/plugins/BasePlugin.sol +++ b/src/plugins/BasePlugin.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/src/plugins/TokenReceiverPlugin.sol b/src/plugins/TokenReceiverPlugin.sol index d4e528c2..9c4654c0 100644 --- a/src/plugins/TokenReceiverPlugin.sol +++ b/src/plugins/TokenReceiverPlugin.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/src/plugins/owner/IMultiOwnerPlugin.sol b/src/plugins/owner/IMultiOwnerPlugin.sol index edfe138b..15427711 100644 --- a/src/plugins/owner/IMultiOwnerPlugin.sol +++ b/src/plugins/owner/IMultiOwnerPlugin.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/src/plugins/owner/MultiOwnerPlugin.sol b/src/plugins/owner/MultiOwnerPlugin.sol index ff9a6d69..5b09240c 100644 --- a/src/plugins/owner/MultiOwnerPlugin.sol +++ b/src/plugins/owner/MultiOwnerPlugin.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/src/plugins/session/ISessionKeyPlugin.sol b/src/plugins/session/ISessionKeyPlugin.sol index 2c2228e9..3654550b 100644 --- a/src/plugins/session/ISessionKeyPlugin.sol +++ b/src/plugins/session/ISessionKeyPlugin.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; @@ -75,7 +75,6 @@ interface ISessionKeyPlugin { error LengthMismatch(); error NativeTokenSpendLimitExceeded(address account, address sessionKey); error SessionKeyNotFound(address sessionKey); - error UnableToRemove(address sessionKey); // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ // ┃ Execution functions ┃ diff --git a/src/plugins/session/SessionKeyPlugin.sol b/src/plugins/session/SessionKeyPlugin.sol index c35239fd..0bdf7721 100644 --- a/src/plugins/session/SessionKeyPlugin.sol +++ b/src/plugins/session/SessionKeyPlugin.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol b/src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol index b6d63c92..a808babb 100644 --- a/src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol +++ b/src/plugins/session/permissions/ISessionKeyPermissionsUpdates.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/src/plugins/session/permissions/SessionKeyPermissions.sol b/src/plugins/session/permissions/SessionKeyPermissions.sol index 6adbedff..cf7eb3bd 100644 --- a/src/plugins/session/permissions/SessionKeyPermissions.sol +++ b/src/plugins/session/permissions/SessionKeyPermissions.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/src/plugins/session/permissions/SessionKeyPermissionsBase.sol b/src/plugins/session/permissions/SessionKeyPermissionsBase.sol index ba9bd955..d19ab92a 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsBase.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsBase.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol b/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol index 7c1c5910..49ede9b8 100644 --- a/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol +++ b/src/plugins/session/permissions/SessionKeyPermissionsLoupe.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/TestUtils.sol b/test/TestUtils.sol index 87d79ab1..ccea8e4e 100644 --- a/test/TestUtils.sol +++ b/test/TestUtils.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/Utils.sol b/test/Utils.sol index 5609d1fd..fb95ef9a 100644 --- a/test/Utils.sol +++ b/test/Utils.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/account/AccountExecHooks.t.sol b/test/account/AccountExecHooks.t.sol index 568d737e..2226587b 100644 --- a/test/account/AccountExecHooks.t.sol +++ b/test/account/AccountExecHooks.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/account/AccountLoupe.t.sol b/test/account/AccountLoupe.t.sol index 3e9b1814..18e74803 100644 --- a/test/account/AccountLoupe.t.sol +++ b/test/account/AccountLoupe.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/account/AccountPreValidationHooks.t.sol b/test/account/AccountPreValidationHooks.t.sol index 62a6decd..ae645343 100644 --- a/test/account/AccountPreValidationHooks.t.sol +++ b/test/account/AccountPreValidationHooks.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/account/AccountReturnData.t.sol b/test/account/AccountReturnData.t.sol index 27cad574..269331f9 100644 --- a/test/account/AccountReturnData.t.sol +++ b/test/account/AccountReturnData.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/account/AccountStorage.t.sol b/test/account/AccountStorage.t.sol index c02c0cb5..ec902ebd 100644 --- a/test/account/AccountStorage.t.sol +++ b/test/account/AccountStorage.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/account/ExecuteFromPluginPermissions.t.sol b/test/account/ExecuteFromPluginPermissions.t.sol index 6d931b5b..54a31967 100644 --- a/test/account/ExecuteFromPluginPermissions.t.sol +++ b/test/account/ExecuteFromPluginPermissions.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/account/ManifestValidity.t.sol b/test/account/ManifestValidity.t.sol index 83f7dc5b..e08ffa64 100644 --- a/test/account/ManifestValidity.t.sol +++ b/test/account/ManifestValidity.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/account/UpgradeableModularAccount.t.sol b/test/account/UpgradeableModularAccount.t.sol index 3fc3dec3..32d21266 100644 --- a/test/account/UpgradeableModularAccount.t.sol +++ b/test/account/UpgradeableModularAccount.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/account/UpgradeableModularAccountPluginManager.t.sol b/test/account/UpgradeableModularAccountPluginManager.t.sol index afd579a5..78e01bfc 100644 --- a/test/account/UpgradeableModularAccountPluginManager.t.sol +++ b/test/account/UpgradeableModularAccountPluginManager.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/account/ValidationIntersection.t.sol b/test/account/ValidationIntersection.t.sol index adf99b83..3c68d7d3 100644 --- a/test/account/ValidationIntersection.t.sol +++ b/test/account/ValidationIntersection.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/account/phases/AccountStatePhases.t.sol b/test/account/phases/AccountStatePhases.t.sol index 89cfbacf..00cbf663 100644 --- a/test/account/phases/AccountStatePhases.t.sol +++ b/test/account/phases/AccountStatePhases.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/account/phases/AccountStatePhasesExec.t.sol b/test/account/phases/AccountStatePhasesExec.t.sol index 0bfeb126..266da386 100644 --- a/test/account/phases/AccountStatePhasesExec.t.sol +++ b/test/account/phases/AccountStatePhasesExec.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/account/phases/AccountStatePhasesRTValidation.t.sol b/test/account/phases/AccountStatePhasesRTValidation.t.sol index f61f244e..a49a1e81 100644 --- a/test/account/phases/AccountStatePhasesRTValidation.t.sol +++ b/test/account/phases/AccountStatePhasesRTValidation.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/account/phases/AccountStatePhasesUOValidation.t.sol b/test/account/phases/AccountStatePhasesUOValidation.t.sol index 88e6b57a..ac56bf14 100644 --- a/test/account/phases/AccountStatePhasesUOValidation.t.sol +++ b/test/account/phases/AccountStatePhasesUOValidation.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/comparison/CompareSimpleAccount.t.sol b/test/comparison/CompareSimpleAccount.t.sol index 0e7945a3..8d23aa54 100644 --- a/test/comparison/CompareSimpleAccount.t.sol +++ b/test/comparison/CompareSimpleAccount.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/factory/MultiOwnerMSCAFactoryTest.t.sol b/test/factory/MultiOwnerMSCAFactoryTest.t.sol index a47f7f6f..6512e5ab 100644 --- a/test/factory/MultiOwnerMSCAFactoryTest.t.sol +++ b/test/factory/MultiOwnerMSCAFactoryTest.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol b/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol index 71eccacb..70b55a60 100644 --- a/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol +++ b/test/factory/MultiOwnerTokenReceiverFactoryTest.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/helpers/FunctionReferenceLib.t.sol b/test/helpers/FunctionReferenceLib.t.sol index f30f3fe7..31cff6be 100644 --- a/test/helpers/FunctionReferenceLib.t.sol +++ b/test/helpers/FunctionReferenceLib.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/helpers/KnownSelectors.t.sol b/test/helpers/KnownSelectors.t.sol index f8db7dc9..e7169d78 100644 --- a/test/helpers/KnownSelectors.t.sol +++ b/test/helpers/KnownSelectors.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/invariant/AssociatedLinkedListSetLibInvariants.t.sol b/test/invariant/AssociatedLinkedListSetLibInvariants.t.sol index cfe6a75f..a0282c83 100644 --- a/test/invariant/AssociatedLinkedListSetLibInvariants.t.sol +++ b/test/invariant/AssociatedLinkedListSetLibInvariants.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/invariant/LLSLRepro.t.sol b/test/invariant/LLSLRepro.t.sol index 518d4c52..cb7aecbd 100644 --- a/test/invariant/LLSLRepro.t.sol +++ b/test/invariant/LLSLRepro.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/invariant/LinkedListSetLibInvariants.t.sol b/test/invariant/LinkedListSetLibInvariants.t.sol index fd8243fd..c8f612ef 100644 --- a/test/invariant/LinkedListSetLibInvariants.t.sol +++ b/test/invariant/LinkedListSetLibInvariants.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/invariant/handlers/AssociatedLinkedListSetHandler.sol b/test/invariant/handlers/AssociatedLinkedListSetHandler.sol index f3814a8c..7e037c7b 100644 --- a/test/invariant/handlers/AssociatedLinkedListSetHandler.sol +++ b/test/invariant/handlers/AssociatedLinkedListSetHandler.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/invariant/handlers/LinkedListSetHandler.sol b/test/invariant/handlers/LinkedListSetHandler.sol index 56b34dac..649a2152 100644 --- a/test/invariant/handlers/LinkedListSetHandler.sol +++ b/test/invariant/handlers/LinkedListSetHandler.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/libraries/AssociatedLinkedListSetLib.t.sol b/test/libraries/AssociatedLinkedListSetLib.t.sol index 3930ad1f..7b295027 100644 --- a/test/libraries/AssociatedLinkedListSetLib.t.sol +++ b/test/libraries/AssociatedLinkedListSetLib.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/libraries/CountableLinkedListSetLib.t.sol b/test/libraries/CountableLinkedListSetLib.t.sol index d7ead39b..267ad83f 100644 --- a/test/libraries/CountableLinkedListSetLib.t.sol +++ b/test/libraries/CountableLinkedListSetLib.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/libraries/LinkedListSetLib.t.sol b/test/libraries/LinkedListSetLib.t.sol index 8c89540b..367fe950 100644 --- a/test/libraries/LinkedListSetLib.t.sol +++ b/test/libraries/LinkedListSetLib.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/libraries/PluginStorageLib.t.sol b/test/libraries/PluginStorageLib.t.sol index 5ae19e81..473514f9 100644 --- a/test/libraries/PluginStorageLib.t.sol +++ b/test/libraries/PluginStorageLib.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/mocks/ContractOwner.sol b/test/mocks/ContractOwner.sol index 9a639249..6dd86957 100644 --- a/test/mocks/ContractOwner.sol +++ b/test/mocks/ContractOwner.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/mocks/Counter.sol b/test/mocks/Counter.sol index b2eb37d9..e0a79208 100644 --- a/test/mocks/Counter.sol +++ b/test/mocks/Counter.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/mocks/Counter.t.sol b/test/mocks/Counter.t.sol index 5c8f5147..b947abe7 100644 --- a/test/mocks/Counter.t.sol +++ b/test/mocks/Counter.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/mocks/MockDiamondStorageContract.sol b/test/mocks/MockDiamondStorageContract.sol index 428f604a..53b6d2f9 100644 --- a/test/mocks/MockDiamondStorageContract.sol +++ b/test/mocks/MockDiamondStorageContract.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/mocks/MockPlugin.sol b/test/mocks/MockPlugin.sol index 94ccfa3b..99ca5696 100644 --- a/test/mocks/MockPlugin.sol +++ b/test/mocks/MockPlugin.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/mocks/plugins/AccountStateMutatingPlugin.sol b/test/mocks/plugins/AccountStateMutatingPlugin.sol index 6bf14797..6f3193be 100644 --- a/test/mocks/plugins/AccountStateMutatingPlugin.sol +++ b/test/mocks/plugins/AccountStateMutatingPlugin.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/mocks/plugins/BadTransferOwnershipPlugin.sol b/test/mocks/plugins/BadTransferOwnershipPlugin.sol index a8b52050..237691ae 100644 --- a/test/mocks/plugins/BadTransferOwnershipPlugin.sol +++ b/test/mocks/plugins/BadTransferOwnershipPlugin.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/mocks/plugins/BaseTestPlugin.sol b/test/mocks/plugins/BaseTestPlugin.sol index 33c26fa6..2dba09bc 100644 --- a/test/mocks/plugins/BaseTestPlugin.sol +++ b/test/mocks/plugins/BaseTestPlugin.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/mocks/plugins/ChangingManifestPlugin.sol b/test/mocks/plugins/ChangingManifestPlugin.sol index 58abcf8a..0b6d0c57 100644 --- a/test/mocks/plugins/ChangingManifestPlugin.sol +++ b/test/mocks/plugins/ChangingManifestPlugin.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/mocks/plugins/ComprehensivePlugin.sol b/test/mocks/plugins/ComprehensivePlugin.sol index 9671e17e..2f588b4f 100644 --- a/test/mocks/plugins/ComprehensivePlugin.sol +++ b/test/mocks/plugins/ComprehensivePlugin.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol b/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol index 27b44c62..ba778283 100644 --- a/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol +++ b/test/mocks/plugins/ExecFromPluginPermissionsMocks.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/mocks/plugins/ManifestValidityMocks.sol b/test/mocks/plugins/ManifestValidityMocks.sol index 30990a67..994a08b7 100644 --- a/test/mocks/plugins/ManifestValidityMocks.sol +++ b/test/mocks/plugins/ManifestValidityMocks.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/mocks/plugins/ReturnDataPluginMocks.sol b/test/mocks/plugins/ReturnDataPluginMocks.sol index 2d25a3fd..ecdad152 100644 --- a/test/mocks/plugins/ReturnDataPluginMocks.sol +++ b/test/mocks/plugins/ReturnDataPluginMocks.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/mocks/plugins/UninstallErrorsPlugin.sol b/test/mocks/plugins/UninstallErrorsPlugin.sol index c9448c28..9c339b1f 100644 --- a/test/mocks/plugins/UninstallErrorsPlugin.sol +++ b/test/mocks/plugins/UninstallErrorsPlugin.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/mocks/plugins/ValidationPluginMocks.sol b/test/mocks/plugins/ValidationPluginMocks.sol index bcc80069..eb66d766 100644 --- a/test/mocks/plugins/ValidationPluginMocks.sol +++ b/test/mocks/plugins/ValidationPluginMocks.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/mocks/tokens/MockERC1155.sol b/test/mocks/tokens/MockERC1155.sol index 6f54cec8..98cd4d72 100644 --- a/test/mocks/tokens/MockERC1155.sol +++ b/test/mocks/tokens/MockERC1155.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/mocks/tokens/MockERC20.sol b/test/mocks/tokens/MockERC20.sol index 619d7171..4cb330d7 100644 --- a/test/mocks/tokens/MockERC20.sol +++ b/test/mocks/tokens/MockERC20.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/mocks/tokens/MockERC777.sol b/test/mocks/tokens/MockERC777.sol index 9bbb66ff..4e0292c0 100644 --- a/test/mocks/tokens/MockERC777.sol +++ b/test/mocks/tokens/MockERC777.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/plugin/TokenReceiverPlugin.t.sol b/test/plugin/TokenReceiverPlugin.t.sol index 9c2ccffa..2415609a 100644 --- a/test/plugin/TokenReceiverPlugin.t.sol +++ b/test/plugin/TokenReceiverPlugin.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/plugin/owner/MultiOwnerPlugin.t.sol b/test/plugin/owner/MultiOwnerPlugin.t.sol index 4ecab62d..2d48b5a0 100644 --- a/test/plugin/owner/MultiOwnerPlugin.t.sol +++ b/test/plugin/owner/MultiOwnerPlugin.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/plugin/owner/MultiOwnerPluginIntegration.t.sol b/test/plugin/owner/MultiOwnerPluginIntegration.t.sol index 71564fb1..8eecb80e 100644 --- a/test/plugin/owner/MultiOwnerPluginIntegration.t.sol +++ b/test/plugin/owner/MultiOwnerPluginIntegration.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol index b12d8812..f08f8964 100644 --- a/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol +++ b/test/plugin/session/SessionKeyPluginWithMultiOwner.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol b/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol index 32d9b911..b386b199 100644 --- a/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyERC20SpendLimits.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/plugin/session/permissions/SessionKeyGasLimits.t.sol b/test/plugin/session/permissions/SessionKeyGasLimits.t.sol index 7c262f1f..795c422c 100644 --- a/test/plugin/session/permissions/SessionKeyGasLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyGasLimits.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol b/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol index 682bdcef..cd863a58 100644 --- a/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol +++ b/test/plugin/session/permissions/SessionKeyNativeTokenSpendLimits.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/plugin/session/permissions/SessionKeyPermissions.t.sol b/test/plugin/session/permissions/SessionKeyPermissions.t.sol index 7b8315b0..e474f6b8 100644 --- a/test/plugin/session/permissions/SessionKeyPermissions.t.sol +++ b/test/plugin/session/permissions/SessionKeyPermissions.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/upgrade/LightAccountToMSCA.t.sol b/test/upgrade/LightAccountToMSCA.t.sol index e6f5c088..867b9fbd 100644 --- a/test/upgrade/LightAccountToMSCA.t.sol +++ b/test/upgrade/LightAccountToMSCA.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; diff --git a/test/upgrade/MSCAToMSCA.t.sol b/test/upgrade/MSCAToMSCA.t.sol index a63fa9ef..59b634fc 100644 --- a/test/upgrade/MSCAToMSCA.t.sol +++ b/test/upgrade/MSCAToMSCA.t.sol @@ -9,10 +9,10 @@ // option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the -// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // -// You should have received a copy of the GNU General Public License along with this program. If not, see +// You should have received a copy of the GNU General Public License along with this program. If not, see // . pragma solidity ^0.8.22; From f1c6a1635e36863579718f0142d400c3f380ee30 Mon Sep 17 00:00:00 2001 From: adam-alchemy <127769144+adam-alchemy@users.noreply.github.com> Date: Tue, 23 Jan 2024 14:43:47 -0800 Subject: [PATCH 102/106] fix: [spearbit-32] Remove extra iteration for length check in AssociatedLinkedListSet (#115) --- src/libraries/AssociatedLinkedListSetLib.sol | 64 ++++++++++++-------- src/libraries/LinkedListSetLib.sol | 3 +- 2 files changed, 39 insertions(+), 28 deletions(-) diff --git a/src/libraries/AssociatedLinkedListSetLib.sol b/src/libraries/AssociatedLinkedListSetLib.sol index bcf6d6c0..5e827af0 100644 --- a/src/libraries/AssociatedLinkedListSetLib.sol +++ b/src/libraries/AssociatedLinkedListSetLib.sol @@ -354,48 +354,60 @@ library AssociatedLinkedListSetLib { /// @notice Gets all elements in a set. /// @dev This is an O(n) operation, where n is the number of elements in the set. /// @param set The set to get the elements of. - /// @return res An array of all elements in the set. + /// @return ret An array of all elements in the set. function getAll(AssociatedLinkedListSet storage set, address associated) internal view - returns (SetValue[] memory res) + returns (SetValue[] memory ret) { TempBytesMemory keyBuffer = _allocateTempKeyBuffer(set, associated); + uint256 size; + bytes32 cursor = _load(_mapLookup(keyBuffer, SENTINEL_VALUE)); + + // Dynamically allocate the returned array as we iterate through the set, since we don't know the size + // beforehand. + // This is accomplished by first writing to memory after the free memory pointer, + // then updating the free memory pointer to cover the newly-allocated data. + // To the compiler, writes to memory after the free memory pointer are considered "memory safe". + // See https://docs.soliditylang.org/en/v0.8.22/assembly.html#memory-safety + // Stack variable lifting done when compiling with via-ir will only ever place variables into memory + // locations below the current free memory pointer, so it is safe to compile this library with via-ir. + // See https://docs.soliditylang.org/en/v0.8.22/yul.html#memoryguard + assembly ("memory-safe") { + // It is critical that no other memory allocations occur between: + // - loading the value of the free memory pointer into `ret` + // - updating the free memory pointer to point to the newly-allocated data, which is done after all + // the values have been written. + ret := mload(0x40) + // Add an extra offset of 4 words to account for the length of the keyBuffer, since it will be used + // for each lookup. If this value were written back to the free memory pointer, it would effectively + // convert the keyBuffer into a "bytes memory" type. However, we don't actually write to the free + // memory pointer until after all we've also allocated the entire return array. + ret := add(ret, 0x80) + } - StoragePointer sentinelSlot = _mapLookup(keyBuffer, SENTINEL_VALUE); - bytes32 cursor = _load(sentinelSlot); - - uint256 count; while (!isSentinel(cursor) && cursor != bytes32(0)) { unchecked { - ++count; + ++size; } bytes32 cleared = clearFlags(cursor); - + // Place the item into the return array manually. Since the size was just incremented, it will point to + // the next location to write to. + assembly ("memory-safe") { + mstore(add(ret, mul(size, 0x20)), cleared) + } if (hasNext(cursor)) { - StoragePointer cursorSlot = _mapLookup(keyBuffer, cleared); - cursor = _load(cursorSlot); + cursor = _load(_mapLookup(keyBuffer, cleared)); } else { cursor = bytes32(0); } } - res = new SetValue[](count); - - if (count == 0) { - return res; - } - - // Re-allocate the key buffer because we just overwrote it! - keyBuffer = _allocateTempKeyBuffer(set, associated); - - cursor = SENTINEL_VALUE; - for (uint256 i = 0; i < count; ++i) { - StoragePointer cursorSlot = _mapLookup(keyBuffer, cursor); - bytes32 cursorValue = _load(cursorSlot); - bytes32 cleared = clearFlags(cursorValue); - res[i] = SetValue.wrap(bytes30(cleared)); - cursor = cleared; + assembly ("memory-safe") { + // Update the free memory pointer with the now-known length of the array. + mstore(0x40, add(ret, mul(add(size, 1), 0x20))) + // Set the length of the array. + mstore(ret, size) } } diff --git a/src/libraries/LinkedListSetLib.sol b/src/libraries/LinkedListSetLib.sol index de42dbbc..5cbace09 100644 --- a/src/libraries/LinkedListSetLib.sol +++ b/src/libraries/LinkedListSetLib.sol @@ -265,8 +265,7 @@ library LinkedListSetLib { // To the compiler, writes to memory after the free memory pointer are considered "memory safe". // See https://docs.soliditylang.org/en/v0.8.22/assembly.html#memory-safety // Stack variable lifting done when compiling with via-ir will only ever place variables into memory - // locations - // below the current free memory pointer, so it is safe to compile this library with via-ir. + // locations below the current free memory pointer, so it is safe to compile this library with via-ir. // See https://docs.soliditylang.org/en/v0.8.22/yul.html#memoryguard assembly ("memory-safe") { // It is critical that no other memory allocations occur between: From 1c71be9ba29bcf02fb9ce4286b37fbcf338d9b98 Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Wed, 24 Jan 2024 12:26:21 -0800 Subject: [PATCH 103/106] chore: add doc for immutable entry point var (#116) --- src/account/UpgradeableModularAccount.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index e51cb0fd..dfabe633 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -77,6 +77,7 @@ contract UpgradeableModularAccount is uint256 callbackGasLimit; } + // ERC-4337 v0.6.0 entrypoint address only IEntryPoint private immutable _ENTRY_POINT; // As per the EIP-165 spec, no interface should ever match 0xffffffff From 1a6b5b7deab2ed49bcefefcc4eaca47427ca92b7 Mon Sep 17 00:00:00 2001 From: howy <132113803+howydev@users.noreply.github.com> Date: Wed, 24 Jan 2024 17:25:06 -0500 Subject: [PATCH 104/106] chore: update readme (#117) Co-authored-by: Jay Paik --- .env.example | 2 +- README.md | 60 +++++++++++++++++++++++----------------------------- 2 files changed, 27 insertions(+), 35 deletions(-) diff --git a/.env.example b/.env.example index f4c33e3f..1bd034f7 100644 --- a/.env.example +++ b/.env.example @@ -16,4 +16,4 @@ ENTRYPOINT= # SESSION_KEY_PLUGIN= # SESSION_KEY_PERMS_PLUGIN= -SEPOLIA_RPC_URL= \ No newline at end of file +RPC_URL= \ No newline at end of file diff --git a/README.md b/README.md index 32b3f13b..7dccecdb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,20 @@ -# Alchemy Modular Smart Contract Account (MSCA) +# Modular Account -Contracts for an upgradeable modular smart contract account that is compatible with ERC-4337, along with a set of plugins. +Alchemy's Modular Account is a maximally modular, upgradeable Smart Contract Account that is compatible with [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337) and [ERC-6900](https://eips.ethereum.org/EIPS/eip-6900). + +## Overview + +This repository contains: +* [Modular Account implementation](src/account) +* [Modular Account factories](src/factory) +* 3 ERC-6900 compatible plugins: + * [MultiOwnerPlugin](src/plugins/owner) is a plugin supporting 1+ ECDSA owners. + * [TokenReceiverPlugin](src/plugins/TokenReceiverPlugin.sol) contains ERC721/ERC777/ERC1155 token receivers. + * [SessionKeyPlugin](src/plugins/session) enables session keys with optional permissions such as time ranges, token spend limits, and gas spend limits. + +The account and plugins conform to these ERC versions: +* ERC-4337: 0.6.0 +* ERC-6900: 0.7.0 ## Development @@ -10,54 +24,32 @@ Contracts for an upgradeable modular smart contract account that is compatible w - `validation` and `validationFunction` are used to replace `validator`. - `associated` and `associatedFunction` are used to represents `validationFunction` and `hook` -## Build +### Building and Testing ```bash +# Build options forge build - -# or use the lite profile to reduce compilation time FOUNDRY_PROFILE=lite forge build - -# for faster IR builds (to check contract sizes) FOUNDRY_PROFILE=optimized-build forge build --sizes -``` - -## Syntax check - -```bash -pnpm lint:src && pnpm lint:test -``` -## Test +# Lint +pnpm lint -```bash +# Test Options forge test -vvv - -# or use the lite profile to reduce compilation time FOUNDRY_PROFILE=lite forge test -vvv ``` -## Generate Inspections - -```bash -bash utils/inspect.sh -``` - -## Static Analysis +### Deployment +A deployment script can be found in the `scripts/` folder ```bash -slither . +forge script script/Deploy.s.sol --rpc-url $RPC_URL --broadcast ``` -## External Libraries - -We use Solady's highly optimized [UUPSUpgradeable](https://github.com/Vectorized/solady/blob/a061f38f27cd7ae330a86d42d3f15b4e7237f064/src/utils/UUPSUpgradeable.sol) in our contracts - -## Deployment +## Security and Audits -```bash -forge script script/Deploy.s.sol --rpc-url $SEPOLIA_RPC_URL --broadcast -``` +We have done 2 audits from Spearbit and Quantstamp and will upload the reports shortly. ## License From 6e7ee3f3dc3557be9d54a922a41d3eccb6b999b2 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Wed, 24 Jan 2024 17:32:08 -0500 Subject: [PATCH 105/106] chore: CC0 license update (#118) --- LICENSE-MIT => LICENSE | 0 LICENSE-CC0 | 121 ------------------- README.md | 4 +- src/interfaces/IAccountInitializable.sol | 5 + src/interfaces/IAccountLoupe.sol | 5 + src/interfaces/IAccountView.sol | 5 + src/interfaces/IPlugin.sol | 7 +- src/interfaces/IPluginExecutor.sol | 5 + src/interfaces/IPluginManager.sol | 5 + src/interfaces/IStandardExecutor.sol | 5 + src/interfaces/erc4337/IAccount.sol | 5 + src/interfaces/erc4337/IAggregator.sol | 5 + src/interfaces/erc4337/IEntryPoint.sol | 5 + src/interfaces/erc4337/IPaymaster.sol | 5 + src/interfaces/erc4337/UserOperation.sol | 5 + src/libraries/AssociatedLinkedListSetLib.sol | 2 +- src/libraries/Constants.sol | 2 +- src/libraries/CountableLinkedListSetLib.sol | 2 +- src/libraries/LinkedListSetLib.sol | 2 +- src/libraries/PluginStorageLib.sol | 2 +- 20 files changed, 68 insertions(+), 129 deletions(-) rename LICENSE-MIT => LICENSE (100%) delete mode 100644 LICENSE-CC0 diff --git a/LICENSE-MIT b/LICENSE similarity index 100% rename from LICENSE-MIT rename to LICENSE diff --git a/LICENSE-CC0 b/LICENSE-CC0 deleted file mode 100644 index 0e259d42..00000000 --- a/LICENSE-CC0 +++ /dev/null @@ -1,121 +0,0 @@ -Creative Commons Legal Code - -CC0 1.0 Universal - - CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE - LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN - ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS - INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES - REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS - PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM - THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED - HEREUNDER. - -Statement of Purpose - -The laws of most jurisdictions throughout the world automatically confer -exclusive Copyright and Related Rights (defined below) upon the creator -and subsequent owner(s) (each and all, an "owner") of an original work of -authorship and/or a database (each, a "Work"). - -Certain owners wish to permanently relinquish those rights to a Work for -the purpose of contributing to a commons of creative, cultural and -scientific works ("Commons") that the public can reliably and without fear -of later claims of infringement build upon, modify, incorporate in other -works, reuse and redistribute as freely as possible in any form whatsoever -and for any purposes, including without limitation commercial purposes. -These owners may contribute to the Commons to promote the ideal of a free -culture and the further production of creative, cultural and scientific -works, or to gain reputation or greater distribution for their Work in -part through the use and efforts of others. - -For these and/or other purposes and motivations, and without any -expectation of additional consideration or compensation, the person -associating CC0 with a Work (the "Affirmer"), to the extent that he or she -is an owner of Copyright and Related Rights in the Work, voluntarily -elects to apply CC0 to the Work and publicly distribute the Work under its -terms, with knowledge of his or her Copyright and Related Rights in the -Work and the meaning and intended legal effect of CC0 on those rights. - -1. Copyright and Related Rights. A Work made available under CC0 may be -protected by copyright and related or neighboring rights ("Copyright and -Related Rights"). Copyright and Related Rights include, but are not -limited to, the following: - - i. the right to reproduce, adapt, distribute, perform, display, - communicate, and translate a Work; - ii. moral rights retained by the original author(s) and/or performer(s); -iii. publicity and privacy rights pertaining to a person's image or - likeness depicted in a Work; - iv. rights protecting against unfair competition in regards to a Work, - subject to the limitations in paragraph 4(a), below; - v. rights protecting the extraction, dissemination, use and reuse of data - in a Work; - vi. database rights (such as those arising under Directive 96/9/EC of the - European Parliament and of the Council of 11 March 1996 on the legal - protection of databases, and under any national implementation - thereof, including any amended or successor version of such - directive); and -vii. other similar, equivalent or corresponding rights throughout the - world based on applicable law or treaty, and any national - implementations thereof. - -2. Waiver. To the greatest extent permitted by, but not in contravention -of, applicable law, Affirmer hereby overtly, fully, permanently, -irrevocably and unconditionally waives, abandons, and surrenders all of -Affirmer's Copyright and Related Rights and associated claims and causes -of action, whether now known or unknown (including existing as well as -future claims and causes of action), in the Work (i) in all territories -worldwide, (ii) for the maximum duration provided by applicable law or -treaty (including future time extensions), (iii) in any current or future -medium and for any number of copies, and (iv) for any purpose whatsoever, -including without limitation commercial, advertising or promotional -purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each -member of the public at large and to the detriment of Affirmer's heirs and -successors, fully intending that such Waiver shall not be subject to -revocation, rescission, cancellation, termination, or any other legal or -equitable action to disrupt the quiet enjoyment of the Work by the public -as contemplated by Affirmer's express Statement of Purpose. - -3. Public License Fallback. Should any part of the Waiver for any reason -be judged legally invalid or ineffective under applicable law, then the -Waiver shall be preserved to the maximum extent permitted taking into -account Affirmer's express Statement of Purpose. In addition, to the -extent the Waiver is so judged Affirmer hereby grants to each affected -person a royalty-free, non transferable, non sublicensable, non exclusive, -irrevocable and unconditional license to exercise Affirmer's Copyright and -Related Rights in the Work (i) in all territories worldwide, (ii) for the -maximum duration provided by applicable law or treaty (including future -time extensions), (iii) in any current or future medium and for any number -of copies, and (iv) for any purpose whatsoever, including without -limitation commercial, advertising or promotional purposes (the -"License"). The License shall be deemed effective as of the date CC0 was -applied by Affirmer to the Work. Should any part of the License for any -reason be judged legally invalid or ineffective under applicable law, such -partial invalidity or ineffectiveness shall not invalidate the remainder -of the License, and in such case Affirmer hereby affirms that he or she -will not (i) exercise any of his or her remaining Copyright and Related -Rights in the Work or (ii) assert any associated claims and causes of -action with respect to the Work, in either case contrary to Affirmer's -express Statement of Purpose. - -4. Limitations and Disclaimers. - - a. No trademark or patent rights held by Affirmer are waived, abandoned, - surrendered, licensed or otherwise affected by this document. - b. Affirmer offers the Work as-is and makes no representations or - warranties of any kind concerning the Work, express, implied, - statutory or otherwise, including without limitation warranties of - title, merchantability, fitness for a particular purpose, non - infringement, or the absence of latent or other defects, accuracy, or - the present or absence of errors, whether or not discoverable, all to - the greatest extent permissible under applicable law. - c. Affirmer disclaims responsibility for clearing rights of other persons - that may apply to the Work or any use thereof, including without - limitation any person's Copyright and Related Rights in the Work. - Further, Affirmer disclaims responsibility for obtaining any necessary - consents, permissions or other rights required for any use of the - Work. - d. Affirmer understands and acknowledges that Creative Commons is not a - party to this document and has no duty or obligation with respect to - this CC0 or use of the Work. diff --git a/README.md b/README.md index 7dccecdb..574d2c04 100644 --- a/README.md +++ b/README.md @@ -53,9 +53,9 @@ We have done 2 audits from Spearbit and Quantstamp and will upload the reports s ## License -The Modular Account libraries (i.e., all code inside the [src/libraries](src/libraries) directory) are licensed under the MIT License, also included in our repository in [LICENSE-MIT](LICENSE-MIT). +The Modular Account libraries (all code inside the [src/libraries](src/libraries) directory) are licensed under the MIT License, also included in our repository in [LICENSE](LICENSE). -The Modular Account and ERC-6900 interfaces (i.e., all code inside the [src/interfaces](src/interfaces) directory) are licensed under the CC0 1.0 Universal License, also included in our repository in [LICENSE-CC0](LICENSE-CC0). +The Modular Account and ERC-6900 interfaces (all code inside the [src/interfaces](src/interfaces) directory) are licensed under the CC0 1.0 Universal License. All other code for Modular Account is licensed under the GNU General Public License v3.0, also included in our repository in [COPYING](COPYING). diff --git a/src/interfaces/IAccountInitializable.sol b/src/interfaces/IAccountInitializable.sol index 8fd0f2c7..02c6b429 100644 --- a/src/interfaces/IAccountInitializable.sol +++ b/src/interfaces/IAccountInitializable.sol @@ -1,4 +1,9 @@ +// This work is marked with CC0 1.0 Universal. +// // SPDX-License-Identifier: CC0-1.0 +// +// To view a copy of this license, visit http://creativecommons.org/publicdomain/zero/1.0 + pragma solidity ^0.8.22; /// @title Account Initializable Interface diff --git a/src/interfaces/IAccountLoupe.sol b/src/interfaces/IAccountLoupe.sol index cb215478..7a519abd 100644 --- a/src/interfaces/IAccountLoupe.sol +++ b/src/interfaces/IAccountLoupe.sol @@ -1,4 +1,9 @@ +// This work is marked with CC0 1.0 Universal. +// // SPDX-License-Identifier: CC0-1.0 +// +// To view a copy of this license, visit http://creativecommons.org/publicdomain/zero/1.0 + pragma solidity ^0.8.22; import {FunctionReference} from "./IPluginManager.sol"; diff --git a/src/interfaces/IAccountView.sol b/src/interfaces/IAccountView.sol index 63d8ff9e..cbc0bf65 100644 --- a/src/interfaces/IAccountView.sol +++ b/src/interfaces/IAccountView.sol @@ -1,4 +1,9 @@ +// This work is marked with CC0 1.0 Universal. +// // SPDX-License-Identifier: CC0-1.0 +// +// To view a copy of this license, visit http://creativecommons.org/publicdomain/zero/1.0 + pragma solidity ^0.8.22; import {IEntryPoint} from "./erc4337/IEntryPoint.sol"; diff --git a/src/interfaces/IPlugin.sol b/src/interfaces/IPlugin.sol index 58f7de70..c5798807 100644 --- a/src/interfaces/IPlugin.sol +++ b/src/interfaces/IPlugin.sol @@ -1,4 +1,9 @@ +// This work is marked with CC0 1.0 Universal. +// // SPDX-License-Identifier: CC0-1.0 +// +// To view a copy of this license, visit http://creativecommons.org/publicdomain/zero/1.0 + pragma solidity ^0.8.22; import {UserOperation} from "../interfaces/erc4337/UserOperation.sol"; @@ -14,7 +19,7 @@ enum ManifestAssociatedFunctionType { // Function belongs to an external plugin provided as a dependency during plugin installation. DEPENDENCY, // Resolves to a magic value to always bypass runtime validation for a given function. - // This is only assignable on runtime validation functions. If it were to be used on a user op validationFunction, + // This is only assignable on runtime validation functions. If it were to be used on a user op validation function, // it would risk burning gas from the account. When used as a hook in any hook location, it is equivalent to not // setting a hook and is therefore disallowed. RUNTIME_VALIDATION_ALWAYS_ALLOW, diff --git a/src/interfaces/IPluginExecutor.sol b/src/interfaces/IPluginExecutor.sol index be1f735f..907917f1 100644 --- a/src/interfaces/IPluginExecutor.sol +++ b/src/interfaces/IPluginExecutor.sol @@ -1,4 +1,9 @@ +// This work is marked with CC0 1.0 Universal. +// // SPDX-License-Identifier: CC0-1.0 +// +// To view a copy of this license, visit http://creativecommons.org/publicdomain/zero/1.0 + pragma solidity ^0.8.22; /// @title Plugin Executor Interface diff --git a/src/interfaces/IPluginManager.sol b/src/interfaces/IPluginManager.sol index a04c3253..01884025 100644 --- a/src/interfaces/IPluginManager.sol +++ b/src/interfaces/IPluginManager.sol @@ -1,4 +1,9 @@ +// This work is marked with CC0 1.0 Universal. +// // SPDX-License-Identifier: CC0-1.0 +// +// To view a copy of this license, visit http://creativecommons.org/publicdomain/zero/1.0 + pragma solidity ^0.8.22; type FunctionReference is bytes21; diff --git a/src/interfaces/IStandardExecutor.sol b/src/interfaces/IStandardExecutor.sol index 710fab2c..bc8eecf5 100644 --- a/src/interfaces/IStandardExecutor.sol +++ b/src/interfaces/IStandardExecutor.sol @@ -1,4 +1,9 @@ +// This work is marked with CC0 1.0 Universal. +// // SPDX-License-Identifier: CC0-1.0 +// +// To view a copy of this license, visit http://creativecommons.org/publicdomain/zero/1.0 + pragma solidity ^0.8.22; struct Call { diff --git a/src/interfaces/erc4337/IAccount.sol b/src/interfaces/erc4337/IAccount.sol index ee6b5c23..907e7b5b 100644 --- a/src/interfaces/erc4337/IAccount.sol +++ b/src/interfaces/erc4337/IAccount.sol @@ -1,4 +1,9 @@ +// This work is marked with CC0 1.0 Universal. +// // SPDX-License-Identifier: CC0-1.0 +// +// To view a copy of this license, visit http://creativecommons.org/publicdomain/zero/1.0 + pragma solidity ^0.8.22; import {IEntryPoint} from "./IEntryPoint.sol"; diff --git a/src/interfaces/erc4337/IAggregator.sol b/src/interfaces/erc4337/IAggregator.sol index 40ca2be2..de00ca5d 100644 --- a/src/interfaces/erc4337/IAggregator.sol +++ b/src/interfaces/erc4337/IAggregator.sol @@ -1,4 +1,9 @@ +// This work is marked with CC0 1.0 Universal. +// // SPDX-License-Identifier: CC0-1.0 +// +// To view a copy of this license, visit http://creativecommons.org/publicdomain/zero/1.0 + pragma solidity ^0.8.22; import {UserOperation} from "./UserOperation.sol"; diff --git a/src/interfaces/erc4337/IEntryPoint.sol b/src/interfaces/erc4337/IEntryPoint.sol index d0f83193..54056c08 100644 --- a/src/interfaces/erc4337/IEntryPoint.sol +++ b/src/interfaces/erc4337/IEntryPoint.sol @@ -1,4 +1,9 @@ +// This work is marked with CC0 1.0 Universal. +// // SPDX-License-Identifier: CC0-1.0 +// +// To view a copy of this license, visit http://creativecommons.org/publicdomain/zero/1.0 + pragma solidity ^0.8.22; import {UserOperation} from "./UserOperation.sol"; diff --git a/src/interfaces/erc4337/IPaymaster.sol b/src/interfaces/erc4337/IPaymaster.sol index 94ad7fb3..1fdf939f 100644 --- a/src/interfaces/erc4337/IPaymaster.sol +++ b/src/interfaces/erc4337/IPaymaster.sol @@ -1,4 +1,9 @@ +// This work is marked with CC0 1.0 Universal. +// // SPDX-License-Identifier: CC0-1.0 +// +// To view a copy of this license, visit http://creativecommons.org/publicdomain/zero/1.0 + pragma solidity ^0.8.22; import {UserOperation} from "./UserOperation.sol"; diff --git a/src/interfaces/erc4337/UserOperation.sol b/src/interfaces/erc4337/UserOperation.sol index 2644fea7..1cad3743 100644 --- a/src/interfaces/erc4337/UserOperation.sol +++ b/src/interfaces/erc4337/UserOperation.sol @@ -1,4 +1,9 @@ +// This work is marked with CC0 1.0 Universal. +// // SPDX-License-Identifier: CC0-1.0 +// +// To view a copy of this license, visit http://creativecommons.org/publicdomain/zero/1.0 + pragma solidity ^0.8.22; /// @notice User Operation struct as defined in ERC-4337 diff --git a/src/libraries/AssociatedLinkedListSetLib.sol b/src/libraries/AssociatedLinkedListSetLib.sol index 5e827af0..55b1c349 100644 --- a/src/libraries/AssociatedLinkedListSetLib.sol +++ b/src/libraries/AssociatedLinkedListSetLib.sol @@ -4,7 +4,7 @@ // // SPDX-License-Identifier: MIT // -// See LICENSE-MIT file for more information +// See LICENSE file for more information pragma solidity ^0.8.22; diff --git a/src/libraries/Constants.sol b/src/libraries/Constants.sol index 192a095d..45644c49 100644 --- a/src/libraries/Constants.sol +++ b/src/libraries/Constants.sol @@ -4,7 +4,7 @@ // // SPDX-License-Identifier: MIT // -// See LICENSE-MIT file for more information +// See LICENSE file for more information pragma solidity ^0.8.22; diff --git a/src/libraries/CountableLinkedListSetLib.sol b/src/libraries/CountableLinkedListSetLib.sol index a29a5aa8..2945d6ef 100644 --- a/src/libraries/CountableLinkedListSetLib.sol +++ b/src/libraries/CountableLinkedListSetLib.sol @@ -4,7 +4,7 @@ // // SPDX-License-Identifier: MIT // -// See LICENSE-MIT file for more information +// See LICENSE file for more information pragma solidity ^0.8.22; diff --git a/src/libraries/LinkedListSetLib.sol b/src/libraries/LinkedListSetLib.sol index 5cbace09..2a6c0525 100644 --- a/src/libraries/LinkedListSetLib.sol +++ b/src/libraries/LinkedListSetLib.sol @@ -4,7 +4,7 @@ // // SPDX-License-Identifier: MIT // -// See LICENSE-MIT file for more information +// See LICENSE file for more information pragma solidity ^0.8.22; diff --git a/src/libraries/PluginStorageLib.sol b/src/libraries/PluginStorageLib.sol index 25f952c8..f6db03f5 100644 --- a/src/libraries/PluginStorageLib.sol +++ b/src/libraries/PluginStorageLib.sol @@ -4,7 +4,7 @@ // // SPDX-License-Identifier: MIT // -// See LICENSE-MIT file for more information +// See LICENSE file for more information pragma solidity ^0.8.22; From 57a9f0297153413ef1e766e4e27ff437cfb35078 Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Wed, 24 Jan 2024 16:12:52 -0800 Subject: [PATCH 106/106] chore: update comments (#119) --- src/interfaces/IPlugin.sol | 5 +++-- src/interfaces/IPluginManager.sol | 1 + src/interfaces/IStandardExecutor.sol | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/interfaces/IPlugin.sol b/src/interfaces/IPlugin.sol index c5798807..0b74df86 100644 --- a/src/interfaces/IPlugin.sol +++ b/src/interfaces/IPlugin.sol @@ -16,7 +16,8 @@ enum ManifestAssociatedFunctionType { NONE, // Function belongs to this plugin. SELF, - // Function belongs to an external plugin provided as a dependency during plugin installation. + // Function belongs to an external plugin provided as a dependency during plugin installation. Plugins MAY depend + // on external validation functions. It MUST NOT depend on external hooks, or installation will fail. DEPENDENCY, // Resolves to a magic value to always bypass runtime validation for a given function. // This is only assignable on runtime validation functions. If it were to be used on a user op validation function, @@ -71,7 +72,7 @@ struct PluginMetadata { // The author field SHOULD be a username representing the identity of the user or organization // that created this plugin. string author; - // String desciptions of the relative sensitivity of specific functions. The selectors MUST be selectors for + // String descriptions of the relative sensitivity of specific functions. The selectors MUST be selectors for // functions implemented by this plugin. SelectorPermission[] permissionDescriptors; } diff --git a/src/interfaces/IPluginManager.sol b/src/interfaces/IPluginManager.sol index 01884025..16648c9b 100644 --- a/src/interfaces/IPluginManager.sol +++ b/src/interfaces/IPluginManager.sol @@ -6,6 +6,7 @@ pragma solidity ^0.8.22; +// Treats the first 20 bytes as an address, and the last byte as a function identifier. type FunctionReference is bytes21; /// @title Plugin Manager Interface diff --git a/src/interfaces/IStandardExecutor.sol b/src/interfaces/IStandardExecutor.sol index bc8eecf5..4bdda531 100644 --- a/src/interfaces/IStandardExecutor.sol +++ b/src/interfaces/IStandardExecutor.sol @@ -9,7 +9,7 @@ pragma solidity ^0.8.22; struct Call { // The target address for the account to call. address target; - // The value sent with the call. + // The value to send with the call. uint256 value; // The calldata for the call. bytes data; @@ -20,7 +20,7 @@ interface IStandardExecutor { /// @notice Standard execute method. /// @dev If the target is a plugin, the call SHOULD revert. /// @param target The target address for the account to call. - /// @param value The value sent with the call. + /// @param value The value to send with the call. /// @param data The calldata for the call. /// @return The return data from the call. function execute(address target, uint256 value, bytes calldata data) external payable returns (bytes memory);