Skip to content

Commit

Permalink
Add 'eip7702Support' config; prototype code for EIP-7702 bundle building
Browse files Browse the repository at this point in the history
  • Loading branch information
forshtat committed Sep 20, 2024
1 parent 1e088a5 commit af61ec4
Show file tree
Hide file tree
Showing 13 changed files with 65 additions and 24 deletions.
8 changes: 6 additions & 2 deletions packages/bundler/src/BundlerConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export interface BundlerConfig {
rip7560: boolean
rip7560Mode: string
gethDevMode: boolean

eip7702Support: boolean
}

// TODO: implement merging config (args -> config.js -> default) and runtime shape validation
Expand All @@ -52,7 +54,8 @@ export const BundlerConfigShape = {
autoBundleMempoolSize: ow.number,
rip7560: ow.boolean,
rip7560Mode: ow.string.oneOf(['PULL', 'PUSH']),
gethDevMode: ow.boolean
gethDevMode: ow.boolean,
eip7702Support: ow.boolean
}

// TODO: consider if we want any default fields at all
Expand All @@ -64,5 +67,6 @@ export const bundlerConfigDefault: Partial<BundlerConfig> = {
unsafe: false,
conditionalRpc: false,
minStake: MIN_STAKE_VALUE,
minUnstakeDelay: MIN_UNSTAKE_DELAY
minUnstakeDelay: MIN_UNSTAKE_DELAY,
eip7702Support: true
}
6 changes: 6 additions & 0 deletions packages/bundler/src/BundlerServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,15 @@ export class BundlerServer {
result = await this.methodHandler.getSupportedEntryPoints()
break
case 'eth_sendUserOperation':
if (!this.config.eip7702Support && params[2] != null) {
throw new Error('EIP-7702 tuples are not supported')
}
result = await this.methodHandler.sendUserOperation(params[0], params[1], params[2])
break
case 'eth_estimateUserOperationGas':
if (!this.config.eip7702Support && params[2] != null) {
throw new Error('EIP-7702 tuples are not supported')
}
result = await this.methodHandler.estimateUserOperationGas(params[0], params[1], params[2])
break
case 'eth_getUserOperationReceipt':
Expand Down
3 changes: 2 additions & 1 deletion packages/bundler/src/MethodHandlerRIP7560.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { BigNumberish } from 'ethers'
import { JsonRpcProvider, TransactionReceipt } from '@ethersproject/providers'
import {
AddressZero,
EIP7702Tuple,
OperationBase,
OperationRIP7560,
StorageMap,
Expand Down Expand Up @@ -33,7 +34,7 @@ export class MethodHandlerRIP7560 {
minBaseFee: BigNumberish,
maxBundleGas: BigNumberish,
maxBundleSize: BigNumberish
): Promise<[OperationBase[], StorageMap]> {
): Promise<[OperationBase[], EIP7702Tuple[], StorageMap]> {
return await this.execManager.createBundle(minBaseFee, maxBundleGas, maxBundleSize)
}

Expand Down
32 changes: 26 additions & 6 deletions packages/bundler/src/modules/BundleManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ import {

import {
AddressZero,
EIP7702Tuple,
IEntryPoint,
OperationBase,
RpcError,
StorageMap,
UserOperation,
ValidationErrors,
getEip7702TupleSigner,
mergeStorageMap,
packUserOp,
runContractScript
Expand All @@ -35,6 +37,9 @@ const debug = Debug('aa.exec.cron')

const THROTTLED_ENTITY_BUNDLE_COUNT = 4

const TX_TYPE_EIP_7702 = 4
const TX_TYPE_EIP_1559 = 2

export interface SendBundleReturn {
transactionHash: string
userOpHashes: string[]
Expand Down Expand Up @@ -77,12 +82,12 @@ export class BundleManager implements IBundleManager {
await this.handlePastEvents()

// TODO: pass correct bundle limit parameters!
const [bundle, storageMap] = await this.createBundle(0, 0, 0)
const [bundle, eip7702Tuples, storageMap] = await this.createBundle(0, 0, 0)
if (bundle.length === 0) {
debug('sendNextBundle - no bundle to send')
} else {
const beneficiary = await this._selectBeneficiary()
const ret = await this.sendBundle(bundle as UserOperation[], beneficiary, storageMap)
const ret = await this.sendBundle(bundle as UserOperation[], eip7702Tuples, beneficiary, storageMap)
debug(`sendNextBundle exit - after sent a bundle of ${bundle.length} `)
return ret
}
Expand All @@ -98,12 +103,13 @@ export class BundleManager implements IBundleManager {
* after submitting the bundle, remove all UserOps from the mempool
* @return SendBundleReturn the transaction and UserOp hashes on successful transaction, or null on failed transaction
*/
async sendBundle (userOps: UserOperation[], beneficiary: string, storageMap: StorageMap): Promise<SendBundleReturn | undefined> {
async sendBundle (userOps: UserOperation[], eip7702Tuples: EIP7702Tuple[], beneficiary: string, storageMap: StorageMap): Promise<SendBundleReturn | undefined> {
try {
const feeData = await this.provider.getFeeData()
// TODO: estimate is not enough. should trace with validation rules, to prevent on-chain revert.
const type = eip7702Tuples.length > 0 ? TX_TYPE_EIP_7702 : TX_TYPE_EIP_1559
const tx = await this.entryPoint.populateTransaction.handleOps(userOps.map(packUserOp), beneficiary, {
type: 2,
type,
nonce: await this.signer.getTransactionCount(),
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? 0,
maxFeePerGas: feeData.maxFeePerGas ?? 0
Expand Down Expand Up @@ -207,9 +213,10 @@ export class BundleManager implements IBundleManager {
minBaseFee?: BigNumberish,
maxBundleGas?: BigNumberish,
maxBundleSize?: BigNumberish
): Promise<[OperationBase[], StorageMap]> {
): Promise<[OperationBase[], EIP7702Tuple[], StorageMap]> {
const entries = this.mempoolManager.getSortedForInclusion()
const bundle: OperationBase[] = []
const eip7702TuplesBundle: EIP7702Tuple[] = []

// paymaster deposit should be enough for all UserOps in the bundle.
const paymasterDeposit: { [paymaster: string]: BigNumber } = {}
Expand Down Expand Up @@ -331,13 +338,26 @@ export class BundleManager implements IBundleManager {
}
mergeStorageMap(storageMap, validationResult.storageMap)

for (const eip7702Tuple of entry.eip7702Tuples) {
const bundleTuple = eip7702TuplesBundle
.find(it => {
return getEip7702TupleSigner(it) === getEip7702TupleSigner(eip7702Tuple)
})
if (bundleTuple != null && bundleTuple.address.toLowerCase() !== eip7702Tuple.address.toLowerCase()) {
debug('unable to add bundle as it relies on an EIP-7702 tuple that conflicts with other UserOperations')
// eslint-disable-next-line no-labels
continue mainLoop
}
}

const newBundleGas = entry.userOpMaxGas.add(bundleGas)
bundleGas = newBundleGas
senders.add(entry.userOp.sender)
bundle.push(entry.userOp)
eip7702TuplesBundle.push(...entry.eip7702Tuples)
totalGas = newTotalGas
}
return [bundle, storageMap]
return [bundle, eip7702TuplesBundle, storageMap]
}

_handleSecondValidationException (e: any, paymaster: string | undefined, entry: MempoolEntry): void {
Expand Down
13 changes: 7 additions & 6 deletions packages/bundler/src/modules/BundleManagerRIP7560.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { RLP } from '@ethereumjs/rlp'
import { hexlify } from 'ethers/lib/utils'

import {
EIP7702Tuple,
OperationBase,
OperationRIP7560,
StorageMap,
Expand Down Expand Up @@ -51,7 +52,7 @@ export class BundleManagerRIP7560 extends BundleManager {
if (bundle.length === 0) {
debug('sendNextBundle - no bundle to send')
} else {
return await this.sendBundle(bundle, '', {})
return await this.sendBundle(bundle, [], '', {})
}
}

Expand All @@ -77,10 +78,10 @@ export class BundleManagerRIP7560 extends BundleManager {
minBaseFee: BigNumberish,
maxBundleGas: BigNumberish,
maxBundleSize: BigNumberish
): Promise<[OperationBase[], StorageMap]> {
const [bundle, storageMap] = await super.createBundle(minBaseFee, maxBundleGas, maxBundleSize)
): Promise<[OperationBase[], EIP7702Tuple[], StorageMap]> {
const [bundle, _, storageMap] = await super.createBundle(minBaseFee, maxBundleGas, maxBundleSize)
if (bundle.length === 0) {
return [[], {}]
return [[], [], {}]
}
const bundleHash = this.computeBundleHash(bundle)

Expand All @@ -94,10 +95,10 @@ export class BundleManagerRIP7560 extends BundleManager {
userOpHashes
})

return [bundle, storageMap]
return [bundle, [], storageMap]
}

async sendBundle (userOps: OperationBase[], _beneficiary: string, _storageMap: StorageMap): Promise<any> {
async sendBundle (userOps: OperationBase[], _eip7702Tuples: EIP7702Tuple[], _beneficiary: string, _storageMap: StorageMap): Promise<any> {
const creationBlock = await this.provider.getBlockNumber()
const bundlerId = 'www.reference-bundler.fake'
const userOpHashes: string[] = []
Expand Down
2 changes: 1 addition & 1 deletion packages/bundler/src/modules/ExecutionManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export class ExecutionManager {
minBaseFee: BigNumberish,
maxBundleGas: BigNumberish,
maxBundleSize: BigNumberish
): Promise<[OperationBase[], StorageMap]> {
): Promise<[OperationBase[], EIP7702Tuple[], StorageMap]> {
return await this.bundleManager.createBundle(minBaseFee, maxBundleGas, maxBundleSize)
}
}
4 changes: 2 additions & 2 deletions packages/bundler/src/modules/IBundleManager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BigNumber, BigNumberish } from 'ethers'

import { OperationBase, StorageMap } from '@account-abstraction/utils'
import { EIP7702Tuple, OperationBase, StorageMap } from '@account-abstraction/utils'

export interface IBundleManager {

Expand All @@ -14,5 +14,5 @@ export interface IBundleManager {
minBaseFee: BigNumberish,
maxBundleGas: BigNumberish,
maxBundleSize: BigNumberish
) => Promise<[OperationBase[], StorageMap]>
) => Promise<[OperationBase[], EIP7702Tuple[], StorageMap]>
}
6 changes: 4 additions & 2 deletions packages/bundler/test/BundlerManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ describe('#BundlerManager', () => {
rip7560: false,
rip7560Mode: 'PULL',
gethDevMode: false,
conditionalRpc: false
conditionalRpc: false,
eip7702Support: false
}

const repMgr = new ReputationManager(provider, BundlerReputationParams, parseEther(config.minStake), config.minUnstakeDelay)
Expand Down Expand Up @@ -108,7 +109,8 @@ describe('#BundlerManager', () => {
rip7560: false,
rip7560Mode: 'PULL',
gethDevMode: false,
minUnstakeDelay: 0
minUnstakeDelay: 0,
eip7702Support: false
}
const repMgr = new ReputationManager(provider, BundlerReputationParams, parseEther(config.minStake), config.minUnstakeDelay)
const mempoolMgr = new MempoolManager(repMgr)
Expand Down
3 changes: 2 additions & 1 deletion packages/bundler/test/BundlerServer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ describe('BundleServer', function () {
rip7560: false,
rip7560Mode: 'PULL',
gethDevMode: false,
minUnstakeDelay: 0
minUnstakeDelay: 0,
eip7702Support: false
}

const repMgr = new ReputationManager(provider, BundlerReputationParams, parseEther(config.minStake), config.minUnstakeDelay)
Expand Down
3 changes: 2 additions & 1 deletion packages/bundler/test/DebugMethodHandler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ describe('#DebugMethodHandler', () => {
rip7560: false,
rip7560Mode: 'PULL',
gethDevMode: false,
minUnstakeDelay: 0
minUnstakeDelay: 0,
eip7702Support: false
}

const repMgr = new ReputationManager(provider, BundlerReputationParams, parseEther(config.minStake), config.minUnstakeDelay)
Expand Down
3 changes: 2 additions & 1 deletion packages/bundler/test/UserOpMethodHandler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ describe('UserOpMethodHandler', function () {
rip7560: false,
rip7560Mode: 'PULL',
gethDevMode: false,
minUnstakeDelay: 0
minUnstakeDelay: 0,
eip7702Support: false
}

const repMgr = new ReputationManager(provider, BundlerReputationParams, parseEther(config.minStake), config.minUnstakeDelay)
Expand Down
2 changes: 1 addition & 1 deletion packages/utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { EIP7702Tuple } from './interfaces/EIP7702Tuple'
export { EIP7702Tuple, getEip7702TupleSigner } from './interfaces/EIP7702Tuple'
export { UserOperation } from './interfaces/UserOperation'
export { OperationBase } from './interfaces/OperationBase'
export { OperationRIP7560 } from './interfaces/OperationRIP7560'
Expand Down
4 changes: 4 additions & 0 deletions packages/utils/src/interfaces/EIP7702Tuple.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ export interface EIP7702Tuple {
r: BigNumberish,
s: BigNumberish
}

export function getEip7702TupleSigner (tuple: EIP7702Tuple): string {
throw new Error('Not implemented')
}

0 comments on commit af61ec4

Please sign in to comment.