From afeca9a32fd34218d13fad02bf19242b9b70a1cc Mon Sep 17 00:00:00 2001 From: Alex Forshtat Date: Thu, 19 Dec 2024 18:34:53 +0100 Subject: [PATCH] Use hardhat impersonating signer to make calls on behalf of the EntryPoint --- test/UserOp.ts | 3 ++- test/entrypoint.test.ts | 2 +- test/entrypointsimulations.test.ts | 1 + test/simple-wallet.test.ts | 35 ++++++++++++++++++++---------- test/testutils.ts | 11 ++++++++-- 5 files changed, 37 insertions(+), 15 deletions(-) diff --git a/test/UserOp.ts b/test/UserOp.ts index 2d8ddd5b2..9720f201c 100644 --- a/test/UserOp.ts +++ b/test/UserOp.ts @@ -155,8 +155,9 @@ export async function fillUserOp (op: Partial, entryPoint?: Entry } if (op1.verificationGasLimit == null) { if (provider == null) throw new Error('no entrypoint/provider') + const senderCreator = await entryPoint?.senderCreator() const initEstimate = await provider.estimateGas({ - from: entryPoint?.address, + from: senderCreator, to: initAddr, data: initCallData, gasLimit: 10e6 diff --git a/test/entrypoint.test.ts b/test/entrypoint.test.ts index aa69f6058..c1dcc732b 100644 --- a/test/entrypoint.test.ts +++ b/test/entrypoint.test.ts @@ -840,7 +840,7 @@ describe('EntryPoint', function () { const senderCreator = SenderCreator__factory.connect(senderCreatorAddress, ethersSigner) await expect( senderCreator.createSender('0xdeadbeef', { gasLimit: 1000000 }) - ).to.be.revertedWith('AAxx should call from EntryPoint') + ).to.be.revertedWith('AA97 should call from EntryPoint') }) it('should reject create if sender address is wrong', async () => { diff --git a/test/entrypointsimulations.test.ts b/test/entrypointsimulations.test.ts index fe3205256..12f1af1c4 100644 --- a/test/entrypointsimulations.test.ts +++ b/test/entrypointsimulations.test.ts @@ -235,6 +235,7 @@ describe('EntryPointSimulations', function () { const { proxy: account } = await createAccount(ethersSigner, await accountOwner.getAddress(), entryPoint.address) const sender = createAddress() const op1 = await fillSignAndPack({ + verificationGasLimit: 150000, // providing default value as gas estimation will fail initCode: hexConcat([ account.address, account.interface.encodeFunctionData('execute', [sender, 0, '0x']) diff --git a/test/simple-wallet.test.ts b/test/simple-wallet.test.ts index 844bd37e8..f8c096633 100644 --- a/test/simple-wallet.test.ts +++ b/test/simple-wallet.test.ts @@ -3,6 +3,7 @@ import { ethers } from 'hardhat' import { expect } from 'chai' import { ERC1967Proxy__factory, + EntryPoint, SimpleAccount, SimpleAccountFactory__factory, SimpleAccount__factory, @@ -12,27 +13,29 @@ import { TestUtil__factory } from '../typechain' import { + HashZero, + ONE_ETH, createAccount, - createAddress, createAccountOwner, + createAddress, + deployEntryPoint, getBalance, - isDeployed, - ONE_ETH, - HashZero, deployEntryPoint + isDeployed } from './testutils' import { fillUserOpDefaults, getUserOpHash, encodeUserOp, signUserOp, packUserOp } from './UserOp' import { parseEther } from 'ethers/lib/utils' import { UserOperation } from './UserOperation' +import { JsonRpcProvider } from '@ethersproject/providers' describe('SimpleAccount', function () { - let entryPoint: string + let entryPoint: EntryPoint let accounts: string[] let testUtil: TestUtil let accountOwner: Wallet const ethersSigner = ethers.provider.getSigner() before(async function () { - entryPoint = await deployEntryPoint().then(e => e.address) + entryPoint = await deployEntryPoint() accounts = await ethers.provider.listAccounts() // ignore in geth.. this is just a sanity test. should be refactored to use a single-account mode.. if (accounts.length < 2) this.skip() @@ -41,12 +44,12 @@ describe('SimpleAccount', function () { }) it('owner should be able to call transfer', async () => { - const { proxy: account } = await createAccount(ethers.provider.getSigner(), accounts[0], entryPoint) + const { proxy: account } = await createAccount(ethers.provider.getSigner(), accounts[0], entryPoint.address) await ethersSigner.sendTransaction({ from: accounts[0], to: account.address, value: parseEther('2') }) await account.execute(accounts[2], ONE_ETH, '0x') }) it('other account should not be able to call transfer', async () => { - const { proxy: account } = await createAccount(ethers.provider.getSigner(), accounts[0], entryPoint) + const { proxy: account } = await createAccount(ethers.provider.getSigner(), accounts[0], entryPoint.address) await expect(account.connect(ethers.provider.getSigner(1)).execute(accounts[2], ONE_ETH, '0x')) .to.be.revertedWith('account: not Owner or EntryPoint') }) @@ -62,7 +65,7 @@ describe('SimpleAccount', function () { let account: SimpleAccount let counter: TestCounter before(async () => { - ({ proxy: account } = await createAccount(ethersSigner, await ethersSigner.getAddress(), entryPoint)) + ({ proxy: account } = await createAccount(ethersSigner, await ethersSigner.getAddress(), entryPoint.address)) counter = await new TestCounter__factory(ethersSigner).deploy() }) @@ -155,9 +158,19 @@ describe('SimpleAccount', function () { }) context('SimpleAccountFactory', () => { - it('sanity: check deployer', async () => { + it('should reject calls coming from any address that is not SenderCreator', async () => { const ownerAddr = createAddress() - const deployer = await new SimpleAccountFactory__factory(ethersSigner).deploy(entryPoint) + let deployer = await new SimpleAccountFactory__factory(ethersSigner).deploy(entryPoint.address) + await expect(deployer.createAccount(ownerAddr, 1234)) + .to.be.revertedWith('only callable from SenderCreator') + + // switch deployer contract to an impersonating signer + const senderCreator = await entryPoint.senderCreator() + await (ethersSigner.provider as JsonRpcProvider).send('hardhat_impersonateAccount', [senderCreator]) + await (ethersSigner.provider as JsonRpcProvider).send('hardhat_setBalance', [senderCreator, parseEther('100').toHexString()]) + const senderCreatorSigner = await ethers.getSigner(senderCreator) + deployer = deployer.connect(senderCreatorSigner) + const target = await deployer.callStatic.createAccount(ownerAddr, 1234) expect(await isDeployed(target)).to.eq(false) await deployer.createAccount(ownerAddr, 1234) diff --git a/test/testutils.ts b/test/testutils.ts index 3e18f7848..40d44c5d9 100644 --- a/test/testutils.ts +++ b/test/testutils.ts @@ -1,7 +1,8 @@ import { ethers } from 'hardhat' import { arrayify, - hexConcat, hexDataSlice, + hexConcat, + hexDataSlice, hexlify, hexZeroPad, Interface, @@ -20,6 +21,7 @@ import { TestAggregatedAccountFactory, TestPaymasterRevertCustomError__factory, TestERC20__factory } from '../typechain' import { BytesLike, Hexable } from '@ethersproject/bytes' +import { JsonRpcProvider } from '@ethersproject/providers' import { expect } from 'chai' import { Create2Factory } from '../src/Create2Factory' import { debugTransaction } from './debugTx' @@ -298,7 +300,12 @@ export async function createAccount ( }> { const accountFactory = _factory ?? await new SimpleAccountFactory__factory(ethersSigner).deploy(entryPoint) const implementation = await accountFactory.accountImplementation() - await accountFactory.createAccount(accountOwner, 0) + const entryPointContract = EntryPoint__factory.connect(entryPoint, ethersSigner) + const senderCreator = await entryPointContract.senderCreator() + await (ethersSigner.provider as JsonRpcProvider).send('hardhat_impersonateAccount', [senderCreator]) + await (ethersSigner.provider as JsonRpcProvider).send('hardhat_setBalance', [senderCreator, parseEther('100').toHexString()]) + const senderCreatorSigner = await ethers.getSigner(senderCreator) + await accountFactory.connect(senderCreatorSigner).createAccount(accountOwner, 0) const accountAddress = await accountFactory.getAddress(accountOwner, 0) const proxy = SimpleAccount__factory.connect(accountAddress, ethersSigner) return {