public UserOperationSimulator( IUserOperationTxBuilder userOperationTxBuilder, IStateProvider stateProvider, IStateReader stateReader, AbiDefinition entryPointContractAbi, Address create2FactoryAddress, Address entryPointContractAddress, ISpecProvider specProvider, IBlockTree blockTree, IDbProvider dbProvider, IReadOnlyTrieStore trieStore, ITimestamper timestamper, ILogManager logManager) { _userOperationTxBuilder = userOperationTxBuilder; _stateProvider = stateProvider; _stateReader = stateReader; _entryPointContractAbi = entryPointContractAbi; _create2FactoryAddress = create2FactoryAddress; _entryPointContractAddress = entryPointContractAddress; _specProvider = specProvider; _blockTree = blockTree; _dbProvider = dbProvider.AsReadOnly(false); _trieStore = trieStore; _timestamper = timestamper; _logManager = logManager; _abiEncoder = new AbiEncoder(); }
public UserOperationTxSource( IUserOperationTxBuilder userOperationTxBuilder, IUserOperationPool userOperationPool, IUserOperationSimulator userOperationSimulator, ISpecProvider specProvider, ILogger logger) { _userOperationTxBuilder = userOperationTxBuilder; _userOperationPool = userOperationPool; _userOperationSimulator = userOperationSimulator; _specProvider = specProvider; _logger = logger; }
public UserOperationSimulator( IUserOperationTxBuilder userOperationTxBuilder, IReadOnlyStateProvider stateProvider, ReadOnlyTxProcessingEnvFactory readOnlyTxProcessingEnvFactory, AbiDefinition entryPointContractAbi, Address entryPointContractAddress, Address[] whitelistedPaymasters, ISpecProvider specProvider, ITimestamper timestamper, ILogManager logManager) { _userOperationTxBuilder = userOperationTxBuilder; _stateProvider = stateProvider; _readOnlyTxProcessingEnvFactory = readOnlyTxProcessingEnvFactory; _entryPointContractAbi = entryPointContractAbi; _entryPointContractAddress = entryPointContractAddress; _whitelistedPaymasters = whitelistedPaymasters; _specProvider = specProvider; _timestamper = timestamper; _logger = logManager.GetClassLogger <UserOperationSimulator>(); _abiEncoder = new AbiEncoder(); }
public IEnumerable <Transaction> GetTransactions(BlockHeader parent, long gasLimit) { IDictionary <Address, HashSet <UInt256> > usedAccessList = new Dictionary <Address, HashSet <UInt256> >(); // IList<UserOperation> userOperationsToInclude = new List<UserOperation>(); IDictionary <Address, IList <UserOperation> > userOperationsToIncludeByEntryPoint = new Dictionary <Address, IList <UserOperation> >(); ulong gasUsed = 0; IList <Tuple <Address, UserOperation> > combinedUserOperations = new List <Tuple <Address, UserOperation> >(); foreach (Address entryPoint in _userOperationPools.Keys) { IEnumerable <UserOperation> entryPointUserOperations = _userOperationPools[entryPoint] .GetUserOperations() .Where(op => op.MaxFeePerGas >= parent.BaseFeePerGas); foreach (UserOperation userOperation in entryPointUserOperations) { combinedUserOperations.Add(Tuple.Create(entryPoint, userOperation)); } } IList <Tuple <Address, UserOperation> > sortedUserOperations = combinedUserOperations.OrderByDescending( op => CalculateUserOperationPremiumGasPrice(op.Item2, parent.BaseFeePerGas)) .ToList(); foreach (Tuple <Address, UserOperation> addressedUserOperation in sortedUserOperations) { (Address entryPoint, UserOperation userOperation) = addressedUserOperation; ulong userOperationTotalGasLimit = (ulong)userOperation.CallGas + (ulong)userOperation.PreVerificationGas + (ulong)userOperation.VerificationGas; if (gasUsed + userOperationTotalGasLimit > (ulong)gasLimit) { continue; } // no intersect of accessed addresses between ops if (userOperation.AccessList.AccessListOverlaps(usedAccessList)) { continue; } // simulate again to make sure the op is still valid ResultWrapper <Keccak> result = _userOperationSimulators[entryPoint].Simulate(userOperation, parent); if (result.Result != Result.Success) { //if (_logger.IsDebug) commented out for testing { _logger.Debug($"UserOperation {userOperation.RequestId!} resimulation unsuccessful: {result.Result.Error}"); // TODO: Remove logging, just for testing _logger.Info($"UserOperation {userOperation.RequestId!} resimulation unsuccessful: {result.Result.Error}"); bool removeResult = _userOperationPools[entryPoint].RemoveUserOperation(userOperation.RequestId !); if (_logger.IsDebug) { _logger.Debug( removeResult ? "Removed UserOperation {userOperation.Hash} from Pool" : "Failed to remove UserOperation {userOperation} from Pool"); } } continue; } gasUsed += userOperationTotalGasLimit; // add user operation with correct entryPoint if (userOperationsToIncludeByEntryPoint.TryGetValue(entryPoint, out IList <UserOperation>?userOperations)) { userOperations.Add(userOperation); } else { userOperationsToIncludeByEntryPoint[entryPoint] = new List <UserOperation> { userOperation }; } // add userOp accessList to combined list foreach (KeyValuePair <Address, HashSet <UInt256> > kv in userOperation.AccessList.Data) { if (usedAccessList.ContainsKey(kv.Key)) { usedAccessList[kv.Key].UnionWith(kv.Value); } else { usedAccessList[kv.Key] = kv.Value; } } } if (userOperationsToIncludeByEntryPoint.Count == 0) { yield break; } UInt256 initialNonce = _stateProvider.GetNonce(_signer.Address); UInt256 txsBuilt = 0; // build transaction for each entryPoint with ops to be included foreach (KeyValuePair <Address, UserOperationTxBuilder> kv in _userOperationTxBuilders) { Address entryPoint = kv.Key; IUserOperationTxBuilder txBuilder = kv.Value; bool foundUserOperations = userOperationsToIncludeByEntryPoint.TryGetValue(entryPoint, out IList <UserOperation>?userOperationsToInclude); if (!foundUserOperations) { continue; } long totalGasUsed = userOperationsToInclude !.Aggregate((long)0, (sum, op) => sum + (long)op.CallGas + (long)op.PreVerificationGas + (long)op.VerificationGas); // build test transaction to make sure it succeeds as a batch of ops Transaction userOperationTransaction = txBuilder.BuildTransactionFromUserOperations( userOperationsToInclude !, parent, totalGasUsed, initialNonce, _specProvider.GetSpec(parent.Number + 1)); if (_logger.IsDebug) { _logger.Debug($"Constructed tx from {userOperationsToInclude!.Count} userOperations: {userOperationTransaction.Hash}"); } // TODO: Remove logging, just for testing _logger.Info($"Constructed tx from {userOperationsToInclude!.Count} userOperations: {userOperationTransaction.Hash}"); BlockchainBridge.CallOutput callOutput = _userOperationSimulators[entryPoint].EstimateGas(parent, userOperationTransaction, CancellationToken.None); FailedOp?failedOp = txBuilder.DecodeEntryPointOutputError(callOutput.OutputData); if (failedOp is not null) { UserOperation opToRemove = userOperationsToInclude[(int)failedOp.Value._opIndex]; _userOperationPools[entryPoint].RemoveUserOperation(opToRemove.RequestId !); continue; } if (callOutput.Error != null) { if (_logger.IsWarn) { _logger.Warn($"AA Simulation error for entryPoint {entryPoint}: {callOutput.Error}"); } continue; } // construct tx with previously estimated gas limit Transaction updatedUserOperationTransaction = _userOperationTxBuilders[entryPoint].BuildTransactionFromUserOperations( userOperationsToInclude, parent, callOutput.GasSpent + 200000, initialNonce + txsBuilt, _specProvider.GetSpec(parent.Number + 1)); txsBuilt++; yield return(updatedUserOperationTransaction); } }