Skip to content

Commit

Permalink
Add EntryPointSimulations contract
Browse files Browse the repository at this point in the history
  • Loading branch information
mmv08 committed Jun 21, 2024
1 parent 4305341 commit 4f04ba5
Show file tree
Hide file tree
Showing 8 changed files with 1,568 additions and 958 deletions.
1 change: 1 addition & 0 deletions modules/4337/contracts/test/Imports.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import "@account-abstraction/contracts/core/EntryPointSimulations.sol";
12 changes: 6 additions & 6 deletions modules/4337/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,27 +50,27 @@
"@account-abstraction/contracts": "^0.7.0",
"@noble/curves": "^1.4.0",
"@nomicfoundation/hardhat-ethers": "^3.0.6",
"@nomicfoundation/hardhat-network-helpers": "^1.0.10",
"@nomicfoundation/hardhat-network-helpers": "^1.0.11",
"@nomicfoundation/hardhat-toolbox": "^5.0.0",
"@openzeppelin/contracts": "^5.0.2",
"@safe-global/safe-4337-local-bundler": "workspace:^0.0.0",
"@safe-global/safe-4337-provider": "workspace:^0.0.0",
"@simplewebauthn/server": "10.0.0",
"@types/chai": "^4.3.16",
"@types/mocha": "^10.0.6",
"@types/node": "^20.14.0",
"@types/node": "^20.14.7",
"@types/yargs": "^17.0.32",
"cbor": "^9.0.2",
"debug": "^4.3.4",
"debug": "^4.3.5",
"dotenv": "^16.4.5",
"ethers": "^6.12.1",
"hardhat": "^2.22.3",
"ethers": "^6.13.1",
"hardhat": "^2.22.5",
"hardhat-deploy": "^0.12.4",
"husky": "^9.0.11",
"solc": "^0.8.25",
"solhint": "^5.0.1",
"ts-node": "^10.9.2",
"typescript": "^5.4.5",
"typescript": "^5.5.2",
"yargs": "^17.7.2"
},
"dependencies": {
Expand Down
19 changes: 19 additions & 0 deletions modules/4337/src/deploy/entrypointHelpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { DeployFunction } from 'hardhat-deploy/types'

const deploy: DeployFunction = async ({ deployments, getNamedAccounts, network }) => {
if (!network.tags.test) {
return
}

const { deployer } = await getNamedAccounts()
const { deploy } = deployments

await deploy('EntryPointSimulations', {
from: deployer,
args: [],
log: true,
deterministicDeployment: true,
})
}

export default deploy
3 changes: 3 additions & 0 deletions modules/4337/src/utils/userOp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ export { PackedUserOperation, UserOperation }
type OptionalExceptFor<T, TRequired extends keyof T = keyof T> = Partial<Pick<T, Exclude<keyof T, TRequired>>> &
Required<Pick<T, TRequired>>

export const PLACEHOLDER_SIGNATURE =
'0x9c8ecb7ad80d2dd4411c8827079cda17095236ee3cba1c9b81153d52af17bc9d0701228dc95a75136a3e3a0130988ba4053cc15d3805db49e2cc08d9c99562191b'

export type SafeUserOperation = {
safe: string
entryPoint: string
Expand Down
65 changes: 55 additions & 10 deletions modules/4337/test/gas/Gas.spec.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
import { expect } from 'chai'
import { deployments, ethers } from 'hardhat'
import { getSafe4337Module, getEntryPoint, getFactory, getSafeModuleSetup, getSafeL2Singleton } from '../utils/setup'
import {
getSafe4337Module,
getEntryPoint,
getFactory,
getSafeModuleSetup,
getSafeL2Singleton,
getEntryPointSimulations,
} from '../utils/setup'
import { buildSignatureBytes, logUserOperationGas } from '../../src/utils/execution'
import { buildPackedUserOperationFromSafeUserOperation, buildSafeUserOpTransaction, signSafeOp } from '../../src/utils/userOp'
import { chainId } from '../utils/encoding'
import { Safe4337 } from '../../src/utils/safe'
import { estimateUserOperationGas } from '../utils/simulations'

describe('Gas Metering', () => {
describe.only('Gas Metering', () => {
const setupTests = deployments.createFixture(async ({ deployments }) => {
await deployments.fixture()
const { HariWillibaldToken, XanderBlazeNFT } = await deployments.run()

const [user] = await ethers.getSigners()
const entryPoint = await getEntryPoint()
const entryPointSimulations = await getEntryPointSimulations()
const module = await getSafe4337Module()
const proxyFactory = await getFactory()
const proxyCreationCode = await proxyFactory.proxyCreationCode()
Expand All @@ -33,6 +42,7 @@ describe('Gas Metering', () => {
return {
user,
entryPoint,
entryPointSimulations,
validator: module,
safe,
erc20Token,
Expand All @@ -42,7 +52,8 @@ describe('Gas Metering', () => {

describe('Safe Deployment + Enabling 4337 Module', () => {
it('Safe with 4337 Module Deployment', async () => {
const { user, entryPoint, validator, safe } = await setupTests()
const { user, entryPoint, entryPointSimulations, validator, safe } = await setupTests()
const entryPointAddress = await entryPoint.getAddress()

// cover the prefund
await user.sendTransaction({ to: safe.address, value: ethers.parseEther('1.0') })
Expand All @@ -61,9 +72,12 @@ describe('Gas Metering', () => {
initCode: safe.getInitCode(),
},
)
const gasEstimation = await estimateUserOperationGas(ethers.provider, entryPointSimulations, safeOp, entryPointAddress)
safeOp.callGasLimit = gasEstimation.callGasLimit
safeOp.preVerificationGas = gasEstimation.preVerificationGas
safeOp.verificationGasLimit = gasEstimation.verificationGasLimit

const signature = buildSignatureBytes([await signSafeOp(user, await validator.getAddress(), safeOp, await chainId())])

const userOp = buildPackedUserOperationFromSafeUserOperation({
safeOp,
signature,
Expand All @@ -77,7 +91,8 @@ describe('Gas Metering', () => {

describe('Safe Deployment + Enabling 4337 Module + Native Transfers', () => {
it('Safe with 4337 Module Deployment + Native Transfer', async () => {
const { user, entryPoint, validator, safe } = await setupTests()
const { user, entryPoint, entryPointSimulations, validator, safe } = await setupTests()
const entryPointAddress = await entryPoint.getAddress()
const amount = ethers.parseEther('0.00001')
const receiver = ethers.Wallet.createRandom().address

Expand All @@ -100,6 +115,10 @@ describe('Gas Metering', () => {
initCode: safe.getInitCode(),
},
)
const gasEstimation = await estimateUserOperationGas(ethers.provider, entryPointSimulations, safeOp, entryPointAddress)
safeOp.callGasLimit = gasEstimation.callGasLimit
safeOp.preVerificationGas = gasEstimation.preVerificationGas
safeOp.verificationGasLimit = gasEstimation.verificationGasLimit

const signature = buildSignatureBytes([await signSafeOp(user, await validator.getAddress(), safeOp, await chainId())])

Expand All @@ -120,7 +139,8 @@ describe('Gas Metering', () => {
})

it('Safe with 4337 Module Native Transfer', async () => {
const { user, entryPoint, validator, safe } = await setupTests()
const { user, entryPoint, entryPointSimulations, validator, safe } = await setupTests()
const entryPointAddress = await getEntryPoint()

await user.sendTransaction({ to: safe.address, value: ethers.parseEther('1.0') })
expect(ethers.dataLength(await ethers.provider.getCode(safe.address))).to.equal(0)
Expand All @@ -141,6 +161,11 @@ describe('Gas Metering', () => {
false,
false,
)
const gasEstimation = await estimateUserOperationGas(ethers.provider, entryPointSimulations, safeOp, entryPointAddress)
safeOp.callGasLimit = gasEstimation.callGasLimit
safeOp.preVerificationGas = gasEstimation.preVerificationGas
safeOp.verificationGasLimit = gasEstimation.verificationGasLimit

const signature = buildSignatureBytes([await signSafeOp(user, await validator.getAddress(), safeOp, await chainId())])
const userOp = buildPackedUserOperationFromSafeUserOperation({
safeOp,
Expand All @@ -155,7 +180,8 @@ describe('Gas Metering', () => {

describe('Safe Deployment + Enabling 4337 Module + Token Operations', () => {
it('Safe with 4337 Module Deployment + ERC20 Token Transfer', async () => {
const { user, entryPoint, validator, safe, erc20Token } = await setupTests()
const { user, entryPoint, entryPointSimulations, validator, safe, erc20Token } = await setupTests()
const entryPointAddress = await entryPoint.getAddress()

await user.sendTransaction({ to: safe.address, value: ethers.parseEther('1.0') })

Expand All @@ -178,6 +204,10 @@ describe('Gas Metering', () => {
initCode: safe.getInitCode(),
},
)
const gasEstimation = await estimateUserOperationGas(ethers.provider, entryPointSimulations, safeOp, entryPointAddress)
safeOp.callGasLimit = gasEstimation.callGasLimit
safeOp.preVerificationGas = gasEstimation.preVerificationGas
safeOp.verificationGasLimit = gasEstimation.verificationGasLimit

const signature = buildSignatureBytes([await signSafeOp(user, await validator.getAddress(), safeOp, await chainId())])

Expand All @@ -196,7 +226,8 @@ describe('Gas Metering', () => {
})

it('Safe with 4337 Module Deployment + ERC721 Token Minting', async () => {
const { user, entryPoint, validator, safe, erc721Token } = await setupTests()
const { user, entryPoint,entryPointSimulations, validator, safe, erc721Token } = await setupTests()
const entryPointAddress = await entryPoint.getAddress()
const tokenID = 1

await user.sendTransaction({ to: safe.address, value: ethers.parseEther('1.0') })
Expand All @@ -216,6 +247,10 @@ describe('Gas Metering', () => {
initCode: safe.getInitCode(),
},
)
const gasEstimation = await estimateUserOperationGas(ethers.provider, entryPointSimulations, safeOp, entryPointAddress)
safeOp.callGasLimit = gasEstimation.callGasLimit
safeOp.preVerificationGas = gasEstimation.preVerificationGas
safeOp.verificationGasLimit = gasEstimation.verificationGasLimit
const signature = buildSignatureBytes([await signSafeOp(user, await validator.getAddress(), safeOp, await chainId())])
const userOp = buildPackedUserOperationFromSafeUserOperation({
safeOp,
Expand All @@ -236,7 +271,8 @@ describe('Gas Metering', () => {

describe('Token Operations Only', () => {
it('Safe with 4337 Module ERC20 Token Transfer', async () => {
const { user, entryPoint, validator, safe, erc20Token } = await setupTests()
const { user, entryPoint, entryPointSimulations, validator, safe, erc20Token } = await setupTests()
const entryPointAddress = await entryPoint.getAddress()

await user.sendTransaction({ to: safe.address, value: ethers.parseEther('1.0') })

Expand All @@ -260,6 +296,10 @@ describe('Gas Metering', () => {
false,
false,
)
const gasEstimation = await estimateUserOperationGas(ethers.provider, entryPointSimulations, safeOp, entryPointAddress)
safeOp.callGasLimit = gasEstimation.callGasLimit
safeOp.preVerificationGas = gasEstimation.preVerificationGas
safeOp.verificationGasLimit = gasEstimation.verificationGasLimit
const signature = buildSignatureBytes([await signSafeOp(user, await validator.getAddress(), safeOp, await chainId())])
const userOp = buildPackedUserOperationFromSafeUserOperation({
safeOp,
Expand All @@ -272,7 +312,8 @@ describe('Gas Metering', () => {
})

it('Safe with 4337 Module ERC721 Token Minting', async () => {
const { user, entryPoint, validator, safe, erc721Token } = await setupTests()
const { user, entryPoint, entryPointSimulations, validator, safe, erc721Token } = await setupTests()
const entryPointAddress = await entryPoint.getAddress()

await user.sendTransaction({ to: safe.address, value: ethers.parseEther('1.0') })

Expand All @@ -294,6 +335,10 @@ describe('Gas Metering', () => {
false,
false,
)
const gasEstimation = await estimateUserOperationGas(ethers.provider, entryPointSimulations, safeOp, entryPointAddress)
safeOp.callGasLimit = gasEstimation.callGasLimit
safeOp.preVerificationGas = gasEstimation.preVerificationGas
safeOp.verificationGasLimit = gasEstimation.verificationGasLimit
const signature = buildSignatureBytes([await signSafeOp(user, await validator.getAddress(), safeOp, await chainId())])
const userOp = buildPackedUserOperationFromSafeUserOperation({
safeOp,
Expand Down
5 changes: 5 additions & 0 deletions modules/4337/test/utils/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ export const getEntryPoint = async () => {
return await ethers.getContractAt('IEntryPoint', EntryPointDeployment.address)
}

export const getEntryPointSimulations = async () => {
const EntryPointDeployment = await deployments.get('EntryPointSimulations')
return await ethers.getContractAt('EntryPointSimulations', EntryPointDeployment.address)
}

export const getSafeAtAddress = async (address: string) => {
return await ethers.getContractAt('SafeMock', address)
}
Expand Down
Loading

0 comments on commit 4f04ba5

Please sign in to comment.