diff --git a/internal/config/values.go b/internal/config/values.go index 35bdb898..932f2314 100644 --- a/internal/config/values.go +++ b/internal/config/values.go @@ -42,8 +42,9 @@ type Values struct { AltMempoolIPFSGateway string AltMempoolIds []string - // Stack Specific gas calculation variables. - IsOpStackNetwork bool + // Rollup related variables. + IsOpStackNetwork bool + IsRIP7212Supported bool // Undocumented variables. DebugMode bool @@ -97,6 +98,7 @@ func GetValues() *Values { viper.SetDefault("erc4337_bundler_blocks_in_the_future", 6) viper.SetDefault("erc4337_bundler_otel_insecure_mode", false) viper.SetDefault("erc4337_bundler_is_op_stack_network", false) + viper.SetDefault("erc4337_bundler_is_rip7212_supported", false) viper.SetDefault("erc4337_bundler_debug_mode", false) viper.SetDefault("erc4337_bundler_gin_mode", gin.ReleaseMode) @@ -134,6 +136,7 @@ func GetValues() *Values { _ = viper.BindEnv("erc4337_bundler_alt_mempool_ipfs_gateway") _ = viper.BindEnv("erc4337_bundler_alt_mempool_ids") _ = viper.BindEnv("erc4337_bundler_is_op_stack_network") + _ = viper.BindEnv("erc4337_bundler_is_rip7212_supported") _ = viper.BindEnv("erc4337_bundler_debug_mode") _ = viper.BindEnv("erc4337_bundler_gin_mode") @@ -194,6 +197,7 @@ func GetValues() *Values { altMempoolIPFSGateway := viper.GetString("erc4337_bundler_alt_mempool_ipfs_gateway") altMempoolIds := envArrayToStringSlice(viper.GetString("erc4337_bundler_alt_mempool_ids")) isOpStackNetwork := viper.GetBool("erc4337_bundler_is_op_stack_network") + isRIP7212Supported := viper.GetBool("erc4337_bundler_is_rip7212_supported") debugMode := viper.GetBool("erc4337_bundler_debug_mode") ginMode := viper.GetString("erc4337_bundler_gin_mode") return &Values{ @@ -218,6 +222,7 @@ func GetValues() *Values { AltMempoolIPFSGateway: altMempoolIPFSGateway, AltMempoolIds: altMempoolIds, IsOpStackNetwork: isOpStackNetwork, + IsRIP7212Supported: isRIP7212Supported, DebugMode: debugMode, GinMode: ginMode, } diff --git a/internal/start/private.go b/internal/start/private.go index 3c0d08c7..758a0d1d 100644 --- a/internal/start/private.go +++ b/internal/start/private.go @@ -115,6 +115,7 @@ func PrivateMode() { alt, conf.MaxVerificationGas, conf.MaxBatchGasLimit, + conf.IsRIP7212Supported, conf.NativeBundlerCollectorTracer, conf.ReputationConstants, ) diff --git a/internal/start/searcher.go b/internal/start/searcher.go index bee00571..70b7d4f0 100644 --- a/internal/start/searcher.go +++ b/internal/start/searcher.go @@ -111,6 +111,7 @@ func SearcherMode() { alt, conf.MaxVerificationGas, conf.MaxBatchGasLimit, + conf.IsRIP7212Supported, conf.NativeBundlerCollectorTracer, conf.ReputationConstants, ) diff --git a/pkg/entrypoint/simulation/simulateutils.go b/pkg/entrypoint/simulation/simulateutils.go index 9f9126e3..c01cfcd0 100644 --- a/pkg/entrypoint/simulation/simulateutils.go +++ b/pkg/entrypoint/simulation/simulateutils.go @@ -36,4 +36,8 @@ var ( revertOpCode = "REVERT" returnOpCode = "RETURN" + + // Precompiled contract that performs secp256r1 signature verification. See + // https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7212.md + rip7212precompile = common.HexToAddress("0x100") ) diff --git a/pkg/entrypoint/simulation/storageslots.go b/pkg/entrypoint/simulation/storageslots.go index 31278abf..1e852358 100644 --- a/pkg/entrypoint/simulation/storageslots.go +++ b/pkg/entrypoint/simulation/storageslots.go @@ -50,9 +50,10 @@ func newStorageSlotsByEntity(stakes EntityStakes, keccak []string) storageSlotsB type storageSlotsValidator struct { // Global parameters - Op *userop.UserOperation - EntryPoint common.Address - AltMempools *altmempools.Directory + Op *userop.UserOperation + EntryPoint common.Address + IsRIP7212Supported bool + AltMempools *altmempools.Directory // Parameters of specific entities required for all validation SenderSlots storageSlots @@ -80,6 +81,10 @@ func isAssociatedWith(entitySlots storageSlots, slot string) bool { return false } +func isRIP7212Call(isRIP7212Supported bool, addr common.Address) bool { + return isRIP7212Supported && addr == rip7212precompile +} + func (v *storageSlotsValidator) Process() ([]string, error) { senderSlots := v.SenderSlots if senderSlots == nil { @@ -92,7 +97,7 @@ func (v *storageSlotsValidator) Process() ([]string, error) { altMempoolIds := []string{} for ca, csi := range v.EntityContractSizeMap { - if ca != v.Op.Sender && csi.ContractSize == 0 { + if ca != v.Op.Sender && csi.ContractSize == 0 && !isRIP7212Call(v.IsRIP7212Supported, ca) { return altMempoolIds, fmt.Errorf( "%s uses %s on an address with no deployed code: %s", v.EntityName, diff --git a/pkg/entrypoint/simulation/tracevalidation.go b/pkg/entrypoint/simulation/tracevalidation.go index 41edd0ac..7e32a8bb 100644 --- a/pkg/entrypoint/simulation/tracevalidation.go +++ b/pkg/entrypoint/simulation/tracevalidation.go @@ -23,13 +23,14 @@ import ( ) type TraceInput struct { - Rpc *rpc.Client - EntryPoint common.Address - Op *userop.UserOperation - ChainID *big.Int - Tracer string - Stakes EntityStakes - AltMempools *altmempools.Directory + Rpc *rpc.Client + EntryPoint common.Address + Op *userop.UserOperation + ChainID *big.Int + IsRIP7212Supported bool + Tracer string + Stakes EntityStakes + AltMempools *altmempools.Directory } type TraceOutput struct { @@ -118,6 +119,7 @@ func TraceSimulateValidation(in *TraceInput) (*TraceOutput, error) { v := &storageSlotsValidator{ Op: in.Op, EntryPoint: in.EntryPoint, + IsRIP7212Supported: in.IsRIP7212Supported, AltMempools: in.AltMempools, SenderSlots: slotsByEntity[in.Op.Sender], FactoryIsStaked: knownEntity["factory"].IsStaked, diff --git a/pkg/modules/checks/standalone.go b/pkg/modules/checks/standalone.go index b753a2f5..cffcd547 100644 --- a/pkg/modules/checks/standalone.go +++ b/pkg/modules/checks/standalone.go @@ -33,6 +33,7 @@ type Standalone struct { alt *altmempools.Directory maxVerificationGas *big.Int maxBatchGasLimit *big.Int + isRIP7212Supported bool tracer string repConst *entities.ReputationConstants } @@ -46,11 +47,23 @@ func New( alt *altmempools.Directory, maxVerificationGas *big.Int, maxBatchGasLimit *big.Int, + isRIP7212Supported bool, tracer string, repConst *entities.ReputationConstants, ) *Standalone { eth := ethclient.NewClient(rpc) - return &Standalone{db, rpc, eth, ov, alt, maxVerificationGas, maxBatchGasLimit, tracer, repConst} + return &Standalone{ + db, + rpc, + eth, + ov, + alt, + maxVerificationGas, + maxBatchGasLimit, + isRIP7212Supported, + tracer, + repConst, + } } // ValidateOpValues returns a UserOpHandler that runs through some first line sanity checks for new UserOps @@ -106,12 +119,13 @@ func (s *Standalone) SimulateOp() modules.UserOpHandlerFunc { }) g.Go(func() error { out, err := simulation.TraceSimulateValidation(&simulation.TraceInput{ - Rpc: s.rpc, - EntryPoint: ctx.EntryPoint, - AltMempools: s.alt, - Op: ctx.UserOp, - ChainID: ctx.ChainID, - Tracer: s.tracer, + Rpc: s.rpc, + EntryPoint: ctx.EntryPoint, + AltMempools: s.alt, + Op: ctx.UserOp, + ChainID: ctx.ChainID, + IsRIP7212Supported: s.isRIP7212Supported, + Tracer: s.tracer, Stakes: simulation.EntityStakes{ ctx.UserOp.Sender: ctx.GetSenderDepositInfo(), ctx.UserOp.GetFactory(): ctx.GetFactoryDepositInfo(),