From 30883f22d693ece6c12bf34b13281ad66d1495b1 Mon Sep 17 00:00:00 2001 From: danilo neves cruz Date: Tue, 31 Dec 2024 00:40:03 +0700 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contracts/package.json | 1 + contracts/remappings.txt | 4 ++- contracts/script/Base.s.sol | 48 ++++++++++++++++++++++++-- contracts/script/Deploy.s.sol | 12 ++++++- contracts/test/ExaAccountFactory.t.sol | 44 +++++++++++++++++------ contracts/test/mocks/Mocks.s.sol | 3 ++ pnpm-lock.yaml | 11 +++++- 7 files changed, 107 insertions(+), 16 deletions(-) diff --git a/contracts/package.json b/contracts/package.json index c52d9c8b..5c43e988 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -28,6 +28,7 @@ "@openzeppelin/contracts-upgradeable": "^5.1.0", "@openzeppelin/contracts-v4": "npm:@openzeppelin/contracts@^4.9.6", "account-abstraction": "eth-infinitism/account-abstraction#v0.6.0", + "create3-factory": "lifinance/create3-factory#3aa5f0c", "forge-std": "foundry-rs/forge-std#v1.9.5", "fresh-crypto-lib": "rdubois-crypto/FreshCryptoLib#fd2a0e6", "solhint": "^5.0.3", diff --git a/contracts/remappings.txt b/contracts/remappings.txt index ec3ea862..c74fb611 100644 --- a/contracts/remappings.txt +++ b/contracts/remappings.txt @@ -11,5 +11,7 @@ webauthn-sol/=node_modules/webauthn-sol/src @openzeppelin/contracts-v4/=node_modules/@openzeppelin/contracts-v4/ @openzeppelin/contracts/=node_modules/@openzeppelin/contracts-v4/ account-abstraction/=node_modules/account-abstraction/contracts/ +create3-factory/=node_modules/create3-factory/src/ forge-std/=node_modules/forge-std/src/ -solmate/=node_modules/solmate/ +solmate/src/=node_modules/solmate/src/ +solmate/utils/=node_modules/solmate/src/utils/ diff --git a/contracts/script/Base.s.sol b/contracts/script/Base.s.sol index 63510ce4..f2e6b6f9 100644 --- a/contracts/script/Base.s.sol +++ b/contracts/script/Base.s.sol @@ -1,7 +1,51 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.0; +import { LibString } from "solady/utils/LibString.sol"; + import { ForkTest, stdError, stdJson } from "../test/Fork.t.sol"; // solhint-disable-line no-unused-import -// solhint-disable-next-line no-empty-blocks -abstract contract BaseScript is ForkTest { } +abstract contract BaseScript is ForkTest { + using LibString for address; + using LibString for bytes; + + ICREATE3Factory internal constant CREATE3_FACTORY = ICREATE3Factory(0x93FEC2C00BfE902F733B57c5a6CeeD7CD1384AE1); + + constructor() { + vm.label(address(CREATE3_FACTORY), "CREATE3Factory"); + if (block.chainid == getChain("anvil").chainId) { + bytes memory code = + hex"6080604052600436106100295760003560e01c806350f1c4641461002e578063cdcb760a14610077575b600080fd5b34801561003a57600080fd5b5061004e610049366004610489565b61008a565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b61004e6100853660046104fd565b6100ee565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084901b166020820152603481018290526000906054016040516020818303038152906040528051906020012091506100e78261014c565b9392505050565b6040517fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003360601b166020820152603481018390526000906054016040516020818303038152906040528051906020012092506100e78383346102b2565b604080518082018252601081527f67363d3d37363d34f03d5260086018f30000000000000000000000000000000060209182015290517fff00000000000000000000000000000000000000000000000000000000000000918101919091527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b166021820152603581018290527f21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f60558201526000908190610228906075015b6040516020818303038152906040528051906020012090565b6040517fd69400000000000000000000000000000000000000000000000000000000000060208201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606083901b1660228201527f010000000000000000000000000000000000000000000000000000000000000060368201529091506100e79060370161020f565b6000806040518060400160405280601081526020017f67363d3d37363d34f03d5260086018f30000000000000000000000000000000081525090506000858251602084016000f5905073ffffffffffffffffffffffffffffffffffffffff811661037d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f4445504c4f594d454e545f4641494c454400000000000000000000000000000060448201526064015b60405180910390fd5b6103868661014c565b925060008173ffffffffffffffffffffffffffffffffffffffff1685876040516103b091906105d6565b60006040518083038185875af1925050503d80600081146103ed576040519150601f19603f3d011682016040523d82523d6000602084013e6103f2565b606091505b50509050808015610419575073ffffffffffffffffffffffffffffffffffffffff84163b15155b61047f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f494e495449414c495a4154494f4e5f4641494c454400000000000000000000006044820152606401610374565b5050509392505050565b6000806040838503121561049c57600080fd5b823573ffffffffffffffffffffffffffffffffffffffff811681146104c057600080fd5b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000806040838503121561051057600080fd5b82359150602083013567ffffffffffffffff8082111561052f57600080fd5b818501915085601f83011261054357600080fd5b813581811115610555576105556104ce565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561059b5761059b6104ce565b816040528281528860208487010111156105b457600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b6000825160005b818110156105f757602081860181015185830152016105dd565b50600092019182525091905056fea26469706673582212201ff95c2aafa102481fdd22c59ee7f98a92a9662a6566ab5e0498e8bb47a5f30c64736f6c63430008110033"; + vm.etch(address(CREATE3_FACTORY), code); + try vm.activeFork() { + vm.rpc( + "anvil_setCode", + string.concat('["', address(CREATE3_FACTORY).toHexString(), '","', code.toHexString(), '"]') // solhint-disable-line quotes + ); + } catch { } // solhint-disable-line no-empty-blocks + } + + if (block.chainid == 31_337) { + vm.etch( + address(CREATE3_FACTORY), + hex"6080604052600436106100295760003560e01c806350f1c4641461002e578063cdcb760a14610077575b600080fd5b34801561003a57600080fd5b5061004e610049366004610489565b61008a565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b61004e6100853660046104fd565b6100ee565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084901b166020820152603481018290526000906054016040516020818303038152906040528051906020012091506100e78261014c565b9392505050565b6040517fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003360601b166020820152603481018390526000906054016040516020818303038152906040528051906020012092506100e78383346102b2565b604080518082018252601081527f67363d3d37363d34f03d5260086018f30000000000000000000000000000000060209182015290517fff00000000000000000000000000000000000000000000000000000000000000918101919091527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b166021820152603581018290527f21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f60558201526000908190610228906075015b6040516020818303038152906040528051906020012090565b6040517fd69400000000000000000000000000000000000000000000000000000000000060208201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606083901b1660228201527f010000000000000000000000000000000000000000000000000000000000000060368201529091506100e79060370161020f565b6000806040518060400160405280601081526020017f67363d3d37363d34f03d5260086018f30000000000000000000000000000000081525090506000858251602084016000f5905073ffffffffffffffffffffffffffffffffffffffff811661037d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f4445504c4f594d454e545f4641494c454400000000000000000000000000000060448201526064015b60405180910390fd5b6103868661014c565b925060008173ffffffffffffffffffffffffffffffffffffffff1685876040516103b091906105d6565b60006040518083038185875af1925050503d80600081146103ed576040519150601f19603f3d011682016040523d82523d6000602084013e6103f2565b606091505b50509050808015610419575073ffffffffffffffffffffffffffffffffffffffff84163b15155b61047f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f494e495449414c495a4154494f4e5f4641494c454400000000000000000000006044820152606401610374565b5050509392505050565b6000806040838503121561049c57600080fd5b823573ffffffffffffffffffffffffffffffffffffffff811681146104c057600080fd5b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000806040838503121561051057600080fd5b82359150602083013567ffffffffffffffff8082111561052f57600080fd5b818501915085601f83011261054357600080fd5b813581811115610555576105556104ce565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561059b5761059b6104ce565b816040528281528860208487010111156105b457600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b6000825160005b818110156105f757602081860181015185830152016105dd565b50600092019182525091905056fea26469706673582212201ff95c2aafa102481fdd22c59ee7f98a92a9662a6566ab5e0498e8bb47a5f30c64736f6c63430008110033" + ); + } + } +} + +interface ICREATE3Factory { + /// @notice Deploys a contract using CREATE3 + /// @dev The provided salt is hashed together with msg.sender to generate the final salt + /// @param salt The deployer-specific salt for determining the deployed contract's address + /// @param creationCode The creation code of the contract to deploy + /// @return deployed The address of the deployed contract + function deploy(bytes32 salt, bytes memory creationCode) external payable returns (address deployed); + + /// @notice Predicts the address of a deployed contract + /// @dev The provided salt is hashed together with the deployer address to generate the final salt + /// @param deployer The deployer account that will call deploy() + /// @param salt The deployer-specific salt for determining the deployed contract's address + /// @return deployed The address of the contract that will be deployed + function getDeployed(address deployer, bytes32 salt) external view returns (address deployed); +} diff --git a/contracts/script/Deploy.s.sol b/contracts/script/Deploy.s.sol index 0e4573f6..84a637f1 100644 --- a/contracts/script/Deploy.s.sol +++ b/contracts/script/Deploy.s.sol @@ -65,7 +65,17 @@ contract DeployScript is BaseScript { swapper, keeper ); - factory = new ExaAccountFactory(admin, ownerPlugin, exaPlugin, ACCOUNT_IMPL, ENTRYPOINT); + factory = ExaAccountFactory( + payable( + CREATE3_FACTORY.deploy( + keccak256(abi.encode(exaPlugin.NAME(), exaPlugin.VERSION())), + abi.encodePacked( + vm.getCode("ExaAccountFactory.sol:ExaAccountFactory"), + abi.encode(admin, ownerPlugin, exaPlugin, ACCOUNT_IMPL, ENTRYPOINT) + ) + ) + ) + ); factory.donateStake{ value: 0.1 ether }(); diff --git a/contracts/test/ExaAccountFactory.t.sol b/contracts/test/ExaAccountFactory.t.sol index 2214e135..2d936413 100644 --- a/contracts/test/ExaAccountFactory.t.sol +++ b/contracts/test/ExaAccountFactory.t.sol @@ -1,28 +1,30 @@ // SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.0; // solhint-disable-line one-contract-per-file -import { Test } from "forge-std/Test.sol"; +import { ForkTest } from "./Fork.t.sol"; import { EntryPoint } from "account-abstraction/core/EntryPoint.sol"; import { UpgradeableModularAccount } from "modular-account/src/account/UpgradeableModularAccount.sol"; -import { IEntryPoint } from "modular-account/src/interfaces/erc4337/IEntryPoint.sol"; import { ERC20 } from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; import { ERC4626 } from "openzeppelin-contracts/contracts/token/ERC20/extensions/ERC4626.sol"; import { FixedPointMathLib } from "solady/utils/FixedPointMathLib.sol"; +import { ACCOUNT_IMPL, ENTRYPOINT } from "webauthn-owner-plugin/../script/Factory.s.sol"; import { MAX_OWNERS } from "webauthn-owner-plugin/IWebauthnOwnerPlugin.sol"; import { OwnersLib } from "webauthn-owner-plugin/OwnersLib.sol"; import { WebauthnOwnerPlugin } from "webauthn-owner-plugin/WebauthnOwnerPlugin.sol"; +import { DeployScript } from "../script/Deploy.s.sol"; import { ExaAccountFactory, ExaAccountInitialized } from "../src/ExaAccountFactory.sol"; import { ExaPlugin, IBalancerVault, IDebtManager, IInstallmentsRouter } from "../src/ExaPlugin.sol"; import { IAuditor, IMarket } from "../src/IExaAccount.sol"; import { IssuerChecker } from "../src/IssuerChecker.sol"; +import { Refunder } from "../src/Refunder.sol"; -contract ExaAccountFactoryTest is Test { +contract ExaAccountFactoryTest is ForkTest { using FixedPointMathLib for uint256; using OwnersLib for address[]; @@ -32,24 +34,29 @@ contract ExaAccountFactoryTest is Test { function setUp() external { ownerPlugin = new WebauthnOwnerPlugin(); - exaPlugin = new ExaPlugin( - address(this), - IAuditor(address(0)), + vm.etch(address(ENTRYPOINT), address(new EntryPoint()).code); + vm.etch(ACCOUNT_IMPL, address(new UpgradeableModularAccount(ENTRYPOINT)).code); + vm.deal(acct("deployer"), 1 ether); + + DeployScript d = new DeployScript(); + d._setUp( + IAuditor(address(this)), IMarket(address(new MockERC4626(new MockERC20()))), IMarket(address(new MockERC4626(new MockERC20()))), - IBalancerVault(address(this)), IDebtManager(address(this)), IInstallmentsRouter(address(this)), + IBalancerVault(address(this)), + ownerPlugin, IssuerChecker(address(this)), + Refunder(address(this)), address(this), address(this), address(this) ); + d.run(); - IEntryPoint entryPoint = IEntryPoint(address(new EntryPoint())); - factory = new ExaAccountFactory( - address(this), ownerPlugin, exaPlugin, address(new UpgradeableModularAccount(entryPoint)), entryPoint - ); + exaPlugin = d.exaPlugin(); + factory = d.factory(); } // solhint-disable func-name-mixedcase @@ -88,6 +95,21 @@ contract ExaAccountFactoryTest is Test { } } + function test_deploy_deploysToSameAddress() external { + address[] memory owners = new address[](1); + owners[0] = address(this); + address account = factory.createAccount(0, owners.toPublicKeys()); + + vm.createSelectFork("optimism", 127_050_624); + + DeployScript d = new DeployScript(); + d.setUp(); + d.run(); + + assertEq(address(d.factory()), address(factory), "different factory address"); + assertEq(factory.getAddress(0, owners.toPublicKeys()), account, "different account address"); + } + // solhint-enable func-name-mixedcase function _sorted(address[] memory owners) internal pure returns (address[] memory) { diff --git a/contracts/test/mocks/Mocks.s.sol b/contracts/test/mocks/Mocks.s.sol index f8462967..933ed9b0 100644 --- a/contracts/test/mocks/Mocks.s.sol +++ b/contracts/test/mocks/Mocks.s.sol @@ -4,6 +4,8 @@ pragma solidity ^0.8.0; import { Auditor, IPriceFeed } from "@exactly/protocol/Auditor.sol"; import { Market } from "@exactly/protocol/Market.sol"; +import { CREATE3Factory } from "create3-factory/CREATE3Factory.sol"; + import { FixedPointMathLib } from "solady/utils/FixedPointMathLib.sol"; import { LibString } from "solady/utils/LibString.sol"; @@ -33,6 +35,7 @@ contract DeployMocks is BaseScript { velodromeFactory = new MockVelodromeFactory(); swapper = new MockSwapper(velodromeFactory); + if (block.chainid == getChain("optimism_sepolia").chainId) new CREATE3Factory(); vm.label(address(velodromeFactory), "VelodromeFactory"); vm.label(address(swapper), "Swapper"); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 726a707e..b4e321c1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -469,6 +469,9 @@ importers: account-abstraction: specifier: eth-infinitism/account-abstraction#v0.6.0 version: accountabstraction@https://codeload.github.com/eth-infinitism/account-abstraction/tar.gz/7174d6d845618dbd11cee68eefa715f5263690b6 + create3-factory: + specifier: lifinance/create3-factory#3aa5f0c + version: create3-factory#3aa5f0c@https://codeload.github.com/lifinance/create3-factory/tar.gz/3aa5f0c forge-std: specifier: foundry-rs/forge-std#v1.9.5 version: https://codeload.github.com/foundry-rs/forge-std/tar.gz/b93cf4bc34ff214c099dc970b153f85ade8c9f66 @@ -2289,7 +2292,7 @@ packages: '@expo/bunyan@4.0.1': resolution: {integrity: sha512-+Lla7nYSiHZirgK+U/uYzsLv/X+HaJienbD5AKX1UQZHYfWaP+9uuQluRB4GrEVWF0GZ7vEVp/jzaOT9k/SQlg==} - engines: {node: '>=0.10.0'} + engines: {'0': node >=0.10.0} '@expo/cli@0.22.7': resolution: {integrity: sha512-aNrUPVFPdIX42Q6UM6qygrN4DUqnXMDS1CnkTfNFVIZWRiJ1TUA05Zk6aF35M674CKd/c/dWHFjmbgjsyN/hEA==} @@ -5333,6 +5336,10 @@ packages: typescript: optional: true + create3-factory#3aa5f0c@https://codeload.github.com/lifinance/create3-factory/tar.gz/3aa5f0c: + resolution: {tarball: https://codeload.github.com/lifinance/create3-factory/tar.gz/3aa5f0c} + version: 0.0.0 + cross-fetch@3.2.0: resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} @@ -16079,6 +16086,8 @@ snapshots: optionalDependencies: typescript: 5.7.2 + create3-factory#3aa5f0c@https://codeload.github.com/lifinance/create3-factory/tar.gz/3aa5f0c: {} + cross-fetch@3.2.0: dependencies: node-fetch: 2.7.0