From 79253aaebef1c0efdb1ba084430babb79ecc28d6 Mon Sep 17 00:00:00 2001 From: Jay Paik Date: Thu, 4 Apr 2024 11:53:16 -0400 Subject: [PATCH] chore: update deploy scripts --- .env.example | 5 ++ .gitignore | 4 +- script/Deploy_LightAccountFactory.s.sol | 57 ++++++++------ ...Deploy_MultiOwnerLightAccountFactory.s.sol | 74 +++++++++++++++++++ script/GetInitCodeHash.s.sol | 42 +++++++++++ test/LightAccount.t.sol | 16 ++-- test/MultiOwnerLightAccount.t.sol | 14 ++-- 7 files changed, 174 insertions(+), 38 deletions(-) create mode 100644 script/Deploy_MultiOwnerLightAccountFactory.s.sol create mode 100644 script/GetInitCodeHash.s.sol diff --git a/.env.example b/.env.example index ebc9b5b..5aeed62 100644 --- a/.env.example +++ b/.env.example @@ -4,3 +4,8 @@ RPC_URL_MAINNET= RPC_URL_GOERLI= ETHERSCAN_API_KEY= + +ENTRYPOINT=0x0000000071727De22E5E9d8BAf0edAc6f37da032 # EntryPoint v0.7 +OWNER=0xDdF32240B4ca3184De7EC8f0D5Aba27dEc8B7A5C +REQUIRED_STAKE_AMOUNT=100000000000000000 # 0.1 ETH +UNSTAKE_DELAY_SEC=86400 # 1 day diff --git a/.gitignore b/.gitignore index 7b12f68..5aeb220 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ cache/ .env broadcast/**/run-latest.json -broadcast/**/dry-run/**/* \ No newline at end of file +broadcast/**/dry-run/**/* + +.DS_Store diff --git a/script/Deploy_LightAccountFactory.s.sol b/script/Deploy_LightAccountFactory.s.sol index 84b93a5..35ced4d 100644 --- a/script/Deploy_LightAccountFactory.s.sol +++ b/script/Deploy_LightAccountFactory.s.sol @@ -7,53 +7,68 @@ import {IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol"; import {LightAccountFactory} from "../src/LightAccountFactory.sol"; -// @notice Deploys LightAccountFactory to the address `0x00004EC70002a32400f8ae005A26081065620D20` -// @dev Note: Script uses EntryPoint at address 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789 +// @notice Deploys LightAccountFactory // @dev To run: `forge script script/Deploy_LightAccountFactory.s.sol:Deploy_LightAccountFactory --broadcast --rpc-url ${RPC_URL} --verify -vvvv` contract Deploy_LightAccountFactory is Script { + // Load entrypoint from env + address public entryPointAddr = vm.envAddress("ENTRYPOINT"); + IEntryPoint public entryPoint = IEntryPoint(payable(entryPointAddr)); + + // Load factory owner from env + address public owner = vm.envAddress("OWNER"); + error InitCodeHashMismatch(bytes32 initCodeHash); error DeployedAddressMismatch(address deployed); function run() public { vm.startBroadcast(); - // Using entryPoint: 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789 - // Correct as of Jan 10 2024, from https://docs.alchemy.com/reference/eth-supportedentrypoints - IEntryPoint entryPoint = IEntryPoint(payable(0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789)); - // Init code hash check - bytes32 initCodeHash = keccak256( - abi.encodePacked(type(LightAccountFactory).creationCode, bytes32(uint256(uint160(address(entryPoint))))) - ); + bytes32 initCodeHash = + keccak256(abi.encodePacked(type(LightAccountFactory).creationCode, abi.encode(owner, entryPoint))); - if (initCodeHash != 0x23fb754854a6aa03057b1bae5d971963d92e534dc714fa59fff6c08a3617ba3e) { + if (initCodeHash != 0xfad339962af095db6ac3163c8504f102c28ae099db994101fbbca18ad0e3005c) { revert InitCodeHashMismatch(initCodeHash); } console.log("********************************"); console.log("******** Deploy Inputs *********"); console.log("********************************"); - console.log("Entrypoint Address is:"); - console.logAddress(address(entryPoint)); + console.log("Owner:", owner); + console.log("Entrypoint:", address(entryPoint)); + console.log(); console.log("********************************"); - console.log("******** Deploy ...... *********"); + console.log("******** Deploying.... *********"); console.log("********************************"); - // TODO: Use environment variable for factory owner. LightAccountFactory factory = new LightAccountFactory{ - salt: 0x4e59b44847b379578588920ca78fbf26c0b4956c5528f3e2f146000008fabf77 - }(msg.sender, entryPoint); + salt: 0x00000000000000000000000000000000000000005f1ffd9d31306e056bcc959b + }(owner, entryPoint); // Deployed address check - if (address(factory) != 0x00004EC70002a32400f8ae005A26081065620D20) { + if (address(factory) != 0x0000000000400CdFef5E2714E63d8040b700BC24) { revert DeployedAddressMismatch(address(factory)); } - console.log("LightAccountFactory address:"); - console.logAddress(address(factory)); + _addStakeForFactory(address(factory)); + + console.log("LightAccountFactory:", address(factory)); + console.log("LightAccount:", address(factory.ACCOUNT_IMPLEMENTATION())); + console.log(); - console.log("Implementation address:"); - console.logAddress(address(factory.ACCOUNT_IMPLEMENTATION())); vm.stopBroadcast(); } + + function _addStakeForFactory(address factoryAddr) internal { + uint32 unstakeDelaySec = uint32(vm.envOr("UNSTAKE_DELAY_SEC", uint32(86400))); + uint256 requiredStakeAmount = vm.envUint("REQUIRED_STAKE_AMOUNT"); + uint256 currentStakedAmount = entryPoint.getDepositInfo(factoryAddr).stake; + uint256 stakeAmount = requiredStakeAmount - currentStakedAmount; + LightAccountFactory(payable(factoryAddr)).addStake{value: stakeAmount}(unstakeDelaySec, stakeAmount); + console.log("******** Add Stake Verify *********"); + console.log("Staked factory: ", factoryAddr); + console.log("Stake amount: ", entryPoint.getDepositInfo(factoryAddr).stake); + console.log("Unstake delay: ", entryPoint.getDepositInfo(factoryAddr).unstakeDelaySec); + console.log("******** Stake Verify Done *********"); + } } diff --git a/script/Deploy_MultiOwnerLightAccountFactory.s.sol b/script/Deploy_MultiOwnerLightAccountFactory.s.sol new file mode 100644 index 0000000..be6e167 --- /dev/null +++ b/script/Deploy_MultiOwnerLightAccountFactory.s.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.19; + +import "forge-std/Script.sol"; + +import {IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol"; + +import {MultiOwnerLightAccountFactory} from "../src/MultiOwnerLightAccountFactory.sol"; + +// @notice Deploys MultiOwnerLightAccountFactory +// @dev To run: `forge script script/Deploy_MultiOwnerLightAccountFactory.s.sol:Deploy_MultiOwnerLightAccountFactory --broadcast --rpc-url ${RPC_URL} --verify -vvvv` +contract Deploy_MultiOwnerLightAccountFactory is Script { + // Load entrypoint from env + address public entryPointAddr = vm.envAddress("ENTRYPOINT"); + IEntryPoint public entryPoint = IEntryPoint(payable(entryPointAddr)); + + // Load factory owner from env + address public owner = vm.envAddress("OWNER"); + + error InitCodeHashMismatch(bytes32 initCodeHash); + error DeployedAddressMismatch(address deployed); + + function run() public { + vm.startBroadcast(); + + // Init code hash check + bytes32 initCodeHash = + keccak256(abi.encodePacked(type(MultiOwnerLightAccountFactory).creationCode, abi.encode(owner, entryPoint))); + + if (initCodeHash != 0x69e0f4a2942425638860e9982bd32f08941a082681e53208de970099f18252cc) { + revert InitCodeHashMismatch(initCodeHash); + } + + console.log("********************************"); + console.log("******** Deploy Inputs *********"); + console.log("********************************"); + console.log("Owner:", owner); + console.log("Entrypoint:", address(entryPoint)); + console.log(); + console.log("********************************"); + console.log("******** Deploying.... *********"); + console.log("********************************"); + + MultiOwnerLightAccountFactory factory = new MultiOwnerLightAccountFactory{ + salt: 0x0000000000000000000000000000000000000000bb3ab048b3f4ef2620ea0163 + }(owner, entryPoint); + + // Deployed address check + if (address(factory) != 0x000000000019d2Ee9F2729A65AfE20bb0020AefC) { + revert DeployedAddressMismatch(address(factory)); + } + + _addStakeForFactory(address(factory)); + + console.log("MultiOwnerLightAccountFactory:", address(factory)); + console.log("MultiOwnerLightAccount:", address(factory.ACCOUNT_IMPLEMENTATION())); + console.log(); + + vm.stopBroadcast(); + } + + function _addStakeForFactory(address factoryAddr) internal { + uint32 unstakeDelaySec = uint32(vm.envOr("UNSTAKE_DELAY_SEC", uint32(86400))); + uint256 requiredStakeAmount = vm.envUint("REQUIRED_STAKE_AMOUNT"); + uint256 currentStakedAmount = entryPoint.getDepositInfo(factoryAddr).stake; + uint256 stakeAmount = requiredStakeAmount - currentStakedAmount; + MultiOwnerLightAccountFactory(payable(factoryAddr)).addStake{value: stakeAmount}(unstakeDelaySec, stakeAmount); + console.log("******** Add Stake Verify *********"); + console.log("Staked factory: ", factoryAddr); + console.log("Stake amount: ", entryPoint.getDepositInfo(factoryAddr).stake); + console.log("Unstake delay: ", entryPoint.getDepositInfo(factoryAddr).unstakeDelaySec); + console.log("******** Stake Verify Done *********"); + } +} diff --git a/script/GetInitCodeHash.s.sol b/script/GetInitCodeHash.s.sol new file mode 100644 index 0000000..5d921e5 --- /dev/null +++ b/script/GetInitCodeHash.s.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.23; + +import {Script} from "forge-std/Script.sol"; +import {console} from "forge-std/Test.sol"; + +import {IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol"; + +import {LightAccountFactory} from "../src/LightAccountFactory.sol"; +import {MultiOwnerLightAccountFactory} from "../src/MultiOwnerLightAccountFactory.sol"; + +contract GetInitCodeHash is Script { + // Load entrypoint from env + address public entryPointAddr = vm.envAddress("ENTRYPOINT"); + IEntryPoint public entryPoint = IEntryPoint(payable(entryPointAddr)); + + // Load factory owner from env + address public owner = vm.envAddress("OWNER"); + + function run() public view { + console.log("******** Calculating Init Code Hashes *********"); + console.log("Chain: ", block.chainid); + console.log("EP: ", entryPointAddr); + console.log("Factory owner: ", owner); + + bytes memory lightAccountFactoryInitCode = + abi.encodePacked(type(LightAccountFactory).creationCode, abi.encode(owner, entryPoint)); + + bytes32 lightAccountFactoryInitCodeHash = keccak256(lightAccountFactoryInitCode); + + console.log("LightAccountFactory init code hash:"); + console.logBytes32(lightAccountFactoryInitCodeHash); + + bytes memory multiOwnerLightAccountFactoryInitCode = + abi.encodePacked(type(MultiOwnerLightAccountFactory).creationCode, abi.encode(owner, entryPoint)); + + bytes32 multiOwnerLightAccountFactoryInitCodeHash = keccak256(multiOwnerLightAccountFactoryInitCode); + + console.log("MultiOwnerLightAccountFactory init code hash:"); + console.logBytes32(multiOwnerLightAccountFactoryInitCodeHash); + } +} diff --git a/test/LightAccount.t.sol b/test/LightAccount.t.sol index 948ddcd..3aa0315 100644 --- a/test/LightAccount.t.sol +++ b/test/LightAccount.t.sol @@ -23,6 +23,8 @@ contract LightAccountTest is Test { uint256 public constant EOA_PRIVATE_KEY = 1; address payable public constant BENEFICIARY = payable(address(0xbe9ef1c1a2ee)); bytes32 internal constant _MESSAGE_TYPEHASH = keccak256("LightAccountMessage(bytes message)"); + address public factoryOwner = 0xDdF32240B4ca3184De7EC8f0D5Aba27dEc8B7A5C; + address public entryPointAddr = 0x0000000071727De22E5E9d8BAf0edAc6f37da032; address public eoaAddress; LightAccount public account; EntryPoint public entryPoint; @@ -35,8 +37,9 @@ contract LightAccountTest is Test { function setUp() public { eoaAddress = vm.addr(EOA_PRIVATE_KEY); - entryPoint = new EntryPoint(); - LightAccountFactory factory = new LightAccountFactory(address(this), entryPoint); + vm.etch(entryPointAddr, address(new EntryPoint()).code); + entryPoint = EntryPoint(payable(entryPointAddr)); + LightAccountFactory factory = new LightAccountFactory(factoryOwner, entryPoint); account = factory.createAccount(eoaAddress, 1); vm.deal(address(account), 1 << 128); lightSwitch = new LightSwitch(); @@ -479,13 +482,8 @@ contract LightAccountTest is Test { function testValidateInitCodeHash() external { assertEq( - keccak256( - abi.encodePacked( - type(LightAccountFactory).creationCode, - bytes32(uint256(uint160(0x0000000071727De22E5E9d8BAf0edAc6f37da032))) - ) - ), - 0x5ad3bccf602cb277e15f7bcac8cd88873618c6f038cbcd490610d91be26fcf34 + keccak256(abi.encodePacked(type(LightAccountFactory).creationCode, abi.encode(factoryOwner, entryPoint))), + 0xfad339962af095db6ac3163c8504f102c28ae099db994101fbbca18ad0e3005c ); } diff --git a/test/MultiOwnerLightAccount.t.sol b/test/MultiOwnerLightAccount.t.sol index 22fc57e..2348535 100644 --- a/test/MultiOwnerLightAccount.t.sol +++ b/test/MultiOwnerLightAccount.t.sol @@ -26,6 +26,8 @@ contract MultiOwnerLightAccountTest is Test { uint256 public constant EOA_PRIVATE_KEY = 1; address payable public constant BENEFICIARY = payable(address(0xbe9ef1c1a2ee)); bytes32 internal constant _MESSAGE_TYPEHASH = keccak256("LightAccountMessage(bytes message)"); + address public factoryOwner = 0xDdF32240B4ca3184De7EC8f0D5Aba27dEc8B7A5C; + address public entryPointAddr = 0x0000000071727De22E5E9d8BAf0edAc6f37da032; address public eoaAddress; MultiOwnerLightAccount public account; MultiOwnerLightAccount public contractOwnedAccount; @@ -39,8 +41,9 @@ contract MultiOwnerLightAccountTest is Test { function setUp() public { eoaAddress = vm.addr(EOA_PRIVATE_KEY); - entryPoint = new EntryPoint(); - MultiOwnerLightAccountFactory factory = new MultiOwnerLightAccountFactory(address(this), entryPoint); + vm.etch(entryPointAddr, address(new EntryPoint()).code); + entryPoint = EntryPoint(payable(entryPointAddr)); + MultiOwnerLightAccountFactory factory = new MultiOwnerLightAccountFactory(factoryOwner, entryPoint); account = factory.createAccountSingle(eoaAddress, 1); vm.deal(address(account), 1 << 128); lightSwitch = new LightSwitch(); @@ -625,12 +628,9 @@ contract MultiOwnerLightAccountTest is Test { function testValidateInitCodeHash() external { assertEq( keccak256( - abi.encodePacked( - type(MultiOwnerLightAccountFactory).creationCode, - bytes32(uint256(uint160(0x0000000071727De22E5E9d8BAf0edAc6f37da032))) - ) + abi.encodePacked(type(MultiOwnerLightAccountFactory).creationCode, abi.encode(factoryOwner, entryPoint)) ), - 0x80c58908b2149ff4b21996454a1ad577de59738d9f23637fe3f01506a8836767 + 0x69e0f4a2942425638860e9982bd32f08941a082681e53208de970099f18252cc ); }