From ba6cd699effef504089775ae8f3857ac9afb522e Mon Sep 17 00:00:00 2001 From: 0xFirekeeper <0xFirekeeper@gmail.com> Date: Sat, 22 Feb 2025 04:37:12 +0700 Subject: [PATCH] Thirdweb Insight Integration Closes TOOL-3499 --- Thirdweb.Console/Program.cs | 40 +++ .../Thirdweb.Indexer/ThirdwebInsight.Types.cs | 121 ++++++++ Thirdweb/Thirdweb.Indexer/ThirdwebInsight.cs | 262 ++++++++++++++++++ Thirdweb/Thirdweb.Utils/Constants.cs | 2 + 4 files changed, 425 insertions(+) create mode 100644 Thirdweb/Thirdweb.Indexer/ThirdwebInsight.Types.cs create mode 100644 Thirdweb/Thirdweb.Indexer/ThirdwebInsight.cs diff --git a/Thirdweb.Console/Program.cs b/Thirdweb.Console/Program.cs index de0dec3a..17b41903 100644 --- a/Thirdweb.Console/Program.cs +++ b/Thirdweb.Console/Program.cs @@ -13,6 +13,7 @@ using Thirdweb; using Thirdweb.AccountAbstraction; using Thirdweb.AI; +using Thirdweb.Indexer; using Thirdweb.Pay; DotEnv.Load(); @@ -40,6 +41,45 @@ #endregion +#region Indexer + +// // Create a ThirdwebInsight instance +// var insight = await ThirdwebInsight.Create(client); + +// // Setup some filters +// var address = await Utils.GetAddressFromENS(client, "vitalik.eth"); +// var chains = new BigInteger[] { 1, 137, 42161 }; + +// // Fetch all token types +// var tokens = await insight.GetTokens(address, chains); +// Console.WriteLine($"ERC20 Count: {tokens.erc20Tokens.Length} | ERC721 Count: {tokens.erc721Tokens.Length} | ERC1155 Count: {tokens.erc1155Tokens.Length}"); + +// // Fetch specific token types +// var erc20Tokens = await insight.GetTokens_ERC20(address, chains); +// Console.WriteLine($"ERC20 Tokens: {JsonConvert.SerializeObject(erc20Tokens, Formatting.Indented)}"); + +// // Fetch specific token types +// var erc721Tokens = await insight.GetTokens_ERC721(address, chains); +// Console.WriteLine($"ERC721 Tokens: {JsonConvert.SerializeObject(erc721Tokens, Formatting.Indented)}"); + +// // Fetch specific token types +// var erc1155Tokens = await insight.GetTokens_ERC1155(address, chains); +// Console.WriteLine($"ERC1155 Tokens: {JsonConvert.SerializeObject(erc1155Tokens, Formatting.Indented)}"); + +// // Fetch events (great amount of optional filters available) +// var events = await insight.GetEvents( +// chainIds: new BigInteger[] { 1 }, // ethereum +// contractAddress: "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d", // bored apes +// eventSignature: "Transfer(address,address,uint256)", // transfer event +// fromTimestamp: Utils.GetUnixTimeStampNow() - 3600, // last hour +// sortBy: SortBy.TransactionIndex, // block number, block timestamp or transaction index +// sortOrder: SortOrder.Desc, // latest first +// limit: 5 // last 5 transfers +// ); +// Console.WriteLine($"Events: {JsonConvert.SerializeObject(events, Formatting.Indented)}"); + +#endregion + #region AI // // Prepare some context diff --git a/Thirdweb/Thirdweb.Indexer/ThirdwebInsight.Types.cs b/Thirdweb/Thirdweb.Indexer/ThirdwebInsight.Types.cs new file mode 100644 index 00000000..d0474b66 --- /dev/null +++ b/Thirdweb/Thirdweb.Indexer/ThirdwebInsight.Types.cs @@ -0,0 +1,121 @@ +using System.Numerics; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Thirdweb.Indexer; + +/// +/// Represents the response model wrapping the result of an API call. +/// +/// The type of the result. +internal class ResponseModel +{ + /// + /// The result returned by the API. + /// + [JsonProperty("data")] + internal T[] Data { get; set; } + + [JsonProperty("aggregations")] + public object Aggregations { get; set; } = null!; + + [JsonProperty("meta")] + public Meta Meta { get; set; } = null!; +} + +public class Token +{ + [JsonProperty("chainId", Required = Required.Always)] + public BigInteger ChainId { get; set; } + + [JsonProperty("balance", Required = Required.Always)] + public BigInteger Balance { get; set; } + + [JsonProperty("tokenAddress", Required = Required.Always)] + public string TokenAddress { get; set; } +} + +public class Token_ERC20 : Token { } + +public class Token_ERC721 : Token { } + +public class Token_ERC1155 : Token +{ + [JsonProperty("tokenId", Required = Required.Always)] + public BigInteger TokenId { get; set; } +} + +public class Event +{ + [JsonProperty("chain_id")] + public BigInteger ChainId { get; set; } + + [JsonProperty("block_number")] + public string BlockNumber { get; set; } = null!; + + [JsonProperty("block_hash")] + public string BlockHash { get; set; } = null!; + + [JsonProperty("block_timestamp")] + public string BlockTimestamp { get; set; } = null!; + + [JsonProperty("transaction_hash")] + public string TransactionHash { get; set; } = null!; + + [JsonProperty("transaction_index")] + public BigInteger TransactionIndex { get; set; } + + [JsonProperty("log_index")] + public BigInteger LogIndex { get; set; } + + [JsonProperty("address")] + public string Address { get; set; } = null!; + + [JsonProperty("data")] + public string Data { get; set; } = null!; + + [JsonProperty("topics")] + public List Topics { get; set; } = new(); + + [JsonProperty("decoded")] + public Decoded Decoded { get; set; } = null!; +} + +public class Decoded +{ + [JsonProperty("name")] + public string Name { get; set; } = null!; + + [JsonProperty("signature")] + public string Signature { get; set; } = null!; + + [JsonProperty("indexedParams")] + public JObject IndexedParams { get; set; } = new(); + + [JsonProperty("nonIndexedParams")] + public JObject NonIndexedParams { get; set; } = new(); +} + +public class Meta +{ + [JsonProperty("chain_ids", Required = Required.Always)] + public List ChainIds { get; set; } = new(); + + [JsonProperty("address")] + public string Address { get; set; } + + [JsonProperty("signature")] + public string Signature { get; set; } + + [JsonProperty("page", Required = Required.Always)] + public BigInteger Page { get; set; } + + [JsonProperty("limit_per_chain", Required = Required.Always)] + public BigInteger LimitPerChain { get; set; } + + [JsonProperty("total_items", Required = Required.Always)] + public BigInteger TotalItems { get; set; } + + [JsonProperty("total_pages", Required = Required.Always)] + public BigInteger TotalPages { get; set; } +} diff --git a/Thirdweb/Thirdweb.Indexer/ThirdwebInsight.cs b/Thirdweb/Thirdweb.Indexer/ThirdwebInsight.cs new file mode 100644 index 00000000..9b32bf1a --- /dev/null +++ b/Thirdweb/Thirdweb.Indexer/ThirdwebInsight.cs @@ -0,0 +1,262 @@ +using System.Numerics; +using Newtonsoft.Json; + +namespace Thirdweb.Indexer; + +public enum SortBy +{ + BlockNumber, + BlockTimestamp, + TransactionIndex, +} + +public enum SortOrder +{ + Asc, + Desc, +} + +public class InsightEvents +{ + public Event[] Events { get; set; } + public Meta Meta { get; set; } +} + +public class ThirdwebInsight +{ + private readonly IThirdwebHttpClient _httpClient; + + internal ThirdwebInsight(ThirdwebClient client) + { + this._httpClient = client.HttpClient; + } + + /// + /// Create a new instance of the ThirdwebInsight class. + /// + /// The ThirdwebClient instance. + /// A new instance of . + public static Task Create(ThirdwebClient client) + { + return Task.FromResult(new ThirdwebInsight(client)); + } + + /// + /// Get the token balances of an address. + /// + /// The address to get the token balances of. + /// The chain IDs to get the token balances from. + /// A tuple containing the ERC20, ERC721, and ERC1155 tokens. + /// Thrown when the owner address is null or empty. + /// Thrown when no chain IDs are provided. + public async Task<(Token_ERC20[] erc20Tokens, Token_ERC721[] erc721Tokens, Token_ERC1155[] erc1155Tokens)> GetTokens(string ownerAddress, BigInteger[] chainIds) + { + if (string.IsNullOrEmpty(ownerAddress)) + { + throw new ArgumentNullException(nameof(ownerAddress)); + } + + if (chainIds.Length == 0) + { + throw new ArgumentException("At least one chain ID must be provided.", nameof(chainIds)); + } + + var erc20Tokens = await this.GetTokens_ERC20(ownerAddress, chainIds).ConfigureAwait(false); + var erc721Tokens = await this.GetTokens_ERC721(ownerAddress, chainIds).ConfigureAwait(false); + var erc1155Tokens = await this.GetTokens_ERC1155(ownerAddress, chainIds).ConfigureAwait(false); + return (erc20Tokens, erc721Tokens, erc1155Tokens); + } + + /// + /// Get the ERC20 tokens of an address. + /// + /// The address to get the ERC20 tokens of. + /// The chain IDs to get the ERC20 tokens from. + /// An array of ERC20 tokens. + /// Thrown when the owner address is null or empty. + /// /// Thrown when no chain IDs are provided. + public async Task GetTokens_ERC20(string ownerAddress, BigInteger[] chainIds) + { + if (string.IsNullOrEmpty(ownerAddress)) + { + throw new ArgumentNullException(nameof(ownerAddress)); + } + + if (chainIds.Length == 0) + { + throw new ArgumentException("At least one chain ID must be provided.", nameof(chainIds)); + } + + var url = AppendChains($"{Constants.INSIGHT_API_URL}/v1/tokens/erc20/{ownerAddress}", chainIds); + var response = await this._httpClient.GetAsync(url).ConfigureAwait(false); + _ = response.EnsureSuccessStatusCode(); + var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + return JsonConvert.DeserializeObject>(responseContent).Data; + } + + /// + /// Get the ERC721 tokens of an address. + /// + /// The address to get the ERC721 tokens of. + /// The chain IDs to get the ERC721 tokens from. + /// An array of ERC721 tokens. + /// Thrown when the owner address is null or empty. + /// /// Thrown when no chain IDs are provided. + public async Task GetTokens_ERC721(string ownerAddress, BigInteger[] chainIds) + { + if (string.IsNullOrEmpty(ownerAddress)) + { + throw new ArgumentNullException(nameof(ownerAddress)); + } + + if (chainIds.Length == 0) + { + throw new ArgumentException("At least one chain ID must be provided.", nameof(chainIds)); + } + + var url = AppendChains($"{Constants.INSIGHT_API_URL}/v1/tokens/erc721/{ownerAddress}", chainIds); + var response = await this._httpClient.GetAsync(url).ConfigureAwait(false); + _ = response.EnsureSuccessStatusCode(); + var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + return JsonConvert.DeserializeObject>(responseContent).Data; + } + + /// + /// Get the ERC1155 tokens of an address. + /// + /// The address to get the ERC1155 tokens of. + /// The chain IDs to get the ERC1155 tokens from. + /// An array of ERC1155 tokens. + /// Thrown when the owner address is null or empty. + /// /// Thrown when no chain IDs are provided. + public async Task GetTokens_ERC1155(string ownerAddress, BigInteger[] chainIds) + { + if (string.IsNullOrEmpty(ownerAddress)) + { + throw new ArgumentNullException(nameof(ownerAddress)); + } + + if (chainIds.Length == 0) + { + throw new ArgumentException("At least one chain ID must be provided.", nameof(chainIds)); + } + + var url = AppendChains($"{Constants.INSIGHT_API_URL}/v1/tokens/erc1155/{ownerAddress}", chainIds); + var response = await this._httpClient.GetAsync(url).ConfigureAwait(false); + _ = response.EnsureSuccessStatusCode(); + var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + return JsonConvert.DeserializeObject>(responseContent).Data; + } + + /// + /// Get events, optionally filtered by contract address, event signature, and more. + /// + /// The chain IDs to get the events from. + /// The contract address to get the events from. (Optional) + /// The event signature to get the events from. (Optional) + /// The starting block number to get the events from. (Optional, if provided, said block is included in query) + /// The ending block number to get the events from. (Optional, if provided, said block is included in query) + /// The starting block timestamp to get the events from. (Optional, if provided, said block is included in query) + /// The ending block timestamp to get the events from. (Optional, if provided, said block is included in query) + /// The field to sort the events by. (Default: BlockNumber) + /// The order to sort the events by. (Default: Desc) + /// The number of events to return. (Default: 20) + /// The page number to return. (Default: 0) + /// Whether to decode the events. (Default: true) + /// The events and metadata as an instance of . + /// Thrown when an event signature is provided without a contract address. + /// /// Thrown when no chain IDs are provided. + public async Task GetEvents( + BigInteger[] chainIds, + string contractAddress = null, + string eventSignature = null, + BigInteger? fromBlock = null, + BigInteger? toBlock = null, + BigInteger? fromTimestamp = null, + BigInteger? toTimestamp = null, + SortBy sortBy = SortBy.BlockNumber, + SortOrder sortOrder = SortOrder.Desc, + int limit = 20, + int page = 0, + bool decode = true + ) + { + if (!string.IsNullOrEmpty(eventSignature) && string.IsNullOrEmpty(contractAddress)) + { + throw new ArgumentException("Contract address must be provided when event signature is provided."); + } + + if (chainIds.Length == 0) + { + throw new ArgumentException("At least one chain ID must be provided.", nameof(chainIds)); + } + + var baseUrl = $"{Constants.INSIGHT_API_URL}/v1/events"; + var url = AppendChains( + !string.IsNullOrEmpty(contractAddress) + ? !string.IsNullOrEmpty(eventSignature) + ? $"{baseUrl}/{contractAddress}/{eventSignature}" + : $"{baseUrl}/{contractAddress}" + : baseUrl, + chainIds + ); + + url += $"&sort_by={SortByToString(sortBy)}"; + url += $"&sort_order={SortOrderToString(sortOrder)}"; + url += $"&limit={limit}"; + url += $"&page={page}"; + url += $"&decode={decode}"; + + if (fromBlock.HasValue) + { + url += $"&filter_block_number_gte={fromBlock}"; + } + + if (toBlock.HasValue) + { + url += $"&filter_block_number_lte={toBlock}"; + } + + if (fromTimestamp.HasValue) + { + url += $"&filter_block_timestamp_gte={fromTimestamp}"; + } + + if (toTimestamp.HasValue) + { + url += $"&filter_block_timestamp_lte={toTimestamp}"; + } + + var response = await this._httpClient.GetAsync(url).ConfigureAwait(false); + _ = response.EnsureSuccessStatusCode(); + var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + var result = JsonConvert.DeserializeObject>(responseContent); + return new InsightEvents { Events = result.Data, Meta = result.Meta, }; + } + + private static string AppendChains(string url, BigInteger[] chainIds) + { + return $"{url}?chain={string.Join("&chain=", chainIds)}"; + } + + private static string SortByToString(SortBy sortBy) + { + return sortBy switch + { + SortBy.BlockNumber => "block_number", + SortBy.BlockTimestamp => "block_timestamp", + SortBy.TransactionIndex => "transaction_index", + _ => throw new ArgumentOutOfRangeException(nameof(sortBy), sortBy, null), + }; + } + + private static string SortOrderToString(SortOrder sortOrder) + { + return sortOrder switch + { + SortOrder.Asc => "asc", + SortOrder.Desc => "desc", + _ => throw new ArgumentOutOfRangeException(nameof(sortOrder), sortOrder, null), + }; + } +} diff --git a/Thirdweb/Thirdweb.Utils/Constants.cs b/Thirdweb/Thirdweb.Utils/Constants.cs index ffdb4060..f4bdfedc 100644 --- a/Thirdweb/Thirdweb.Utils/Constants.cs +++ b/Thirdweb/Thirdweb.Utils/Constants.cs @@ -39,6 +39,8 @@ public static class Constants internal const string NEBULA_API_URL = "https://nebula-api.thirdweb.com"; internal const string NEBULA_DEFAULT_MODEL = "t0-001"; + internal const string INSIGHT_API_URL = "https://insight.thirdweb.com"; + internal const string ENTRYPOINT_V06_ABI = /*lang=json,strict*/ "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"preOpGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"paid\",\"type\":\"uint256\"},{\"internalType\":\"uint48\",\"name\":\"validAfter\",\"type\":\"uint48\"},{\"internalType\":\"uint48\",\"name\":\"validUntil\",\"type\":\"uint48\"},{\"internalType\":\"bool\",\"name\":\"targetSuccess\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"targetResult\",\"type\":\"bytes\"}],\"name\":\"ExecutionResult\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"opIndex\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"reason\",\"type\":\"string\"}],\"name\":\"FailedOp\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"SenderAddressResult\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"aggregator\",\"type\":\"address\"}],\"name\":\"SignatureValidationFailed\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"preOpGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"prefund\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"sigFailed\",\"type\":\"bool\"},{\"internalType\":\"uint48\",\"name\":\"validAfter\",\"type\":\"uint48\"},{\"internalType\":\"uint48\",\"name\":\"validUntil\",\"type\":\"uint48\"},{\"internalType\":\"bytes\",\"name\":\"paymasterContext\",\"type\":\"bytes\"}],\"internalType\":\"struct IEntryPoint.ReturnInfo\",\"name\":\"returnInfo\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"stake\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"unstakeDelaySec\",\"type\":\"uint256\"}],\"internalType\":\"struct IStakeManager.StakeInfo\",\"name\":\"senderInfo\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"stake\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"unstakeDelaySec\",\"type\":\"uint256\"}],\"internalType\":\"struct IStakeManager.StakeInfo\",\"name\":\"factoryInfo\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"stake\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"unstakeDelaySec\",\"type\":\"uint256\"}],\"internalType\":\"struct IStakeManager.StakeInfo\",\"name\":\"paymasterInfo\",\"type\":\"tuple\"}],\"name\":\"ValidationResult\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"preOpGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"prefund\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"sigFailed\",\"type\":\"bool\"},{\"internalType\":\"uint48\",\"name\":\"validAfter\",\"type\":\"uint48\"},{\"internalType\":\"uint48\",\"name\":\"validUntil\",\"type\":\"uint48\"},{\"internalType\":\"bytes\",\"name\":\"paymasterContext\",\"type\":\"bytes\"}],\"internalType\":\"struct IEntryPoint.ReturnInfo\",\"name\":\"returnInfo\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"stake\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"unstakeDelaySec\",\"type\":\"uint256\"}],\"internalType\":\"struct IStakeManager.StakeInfo\",\"name\":\"senderInfo\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"stake\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"unstakeDelaySec\",\"type\":\"uint256\"}],\"internalType\":\"struct IStakeManager.StakeInfo\",\"name\":\"factoryInfo\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"stake\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"unstakeDelaySec\",\"type\":\"uint256\"}],\"internalType\":\"struct IStakeManager.StakeInfo\",\"name\":\"paymasterInfo\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"aggregator\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"stake\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"unstakeDelaySec\",\"type\":\"uint256\"}],\"internalType\":\"struct IStakeManager.StakeInfo\",\"name\":\"stakeInfo\",\"type\":\"tuple\"}],\"internalType\":\"struct IEntryPoint.AggregatorStakeInfo\",\"name\":\"aggregatorInfo\",\"type\":\"tuple\"}],\"name\":\"ValidationResultWithAggregation\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"userOpHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"factory\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"paymaster\",\"type\":\"address\"}],\"name\":\"AccountDeployed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"BeforeExecution\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"totalDeposit\",\"type\":\"uint256\"}],\"name\":\"Deposited\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"aggregator\",\"type\":\"address\"}],\"name\":\"SignatureAggregatorChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"totalStaked\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"unstakeDelaySec\",\"type\":\"uint256\"}],\"name\":\"StakeLocked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"withdrawTime\",\"type\":\"uint256\"}],\"name\":\"StakeUnlocked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"withdrawAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"StakeWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"userOpHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"paymaster\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"actualGasCost\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"actualGasUsed\",\"type\":\"uint256\"}],\"name\":\"UserOperationEvent\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"userOpHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"revertReason\",\"type\":\"bytes\"}],\"name\":\"UserOperationRevertReason\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"withdrawAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Withdrawn\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"SIG_VALIDATION_FAILED\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"initCode\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"paymasterAndData\",\"type\":\"bytes\"}],\"name\":\"_validateSenderAndPaymaster\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"unstakeDelaySec\",\"type\":\"uint32\"}],\"name\":\"addStake\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"depositTo\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"deposits\",\"outputs\":[{\"internalType\":\"uint112\",\"name\":\"deposit\",\"type\":\"uint112\"},{\"internalType\":\"bool\",\"name\":\"staked\",\"type\":\"bool\"},{\"internalType\":\"uint112\",\"name\":\"stake\",\"type\":\"uint112\"},{\"internalType\":\"uint32\",\"name\":\"unstakeDelaySec\",\"type\":\"uint32\"},{\"internalType\":\"uint48\",\"name\":\"withdrawTime\",\"type\":\"uint48\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"getDepositInfo\",\"outputs\":[{\"components\":[{\"internalType\":\"uint112\",\"name\":\"deposit\",\"type\":\"uint112\"},{\"internalType\":\"bool\",\"name\":\"staked\",\"type\":\"bool\"},{\"internalType\":\"uint112\",\"name\":\"stake\",\"type\":\"uint112\"},{\"internalType\":\"uint32\",\"name\":\"unstakeDelaySec\",\"type\":\"uint32\"},{\"internalType\":\"uint48\",\"name\":\"withdrawTime\",\"type\":\"uint48\"}],\"internalType\":\"struct IStakeManager.DepositInfo\",\"name\":\"info\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint192\",\"name\":\"key\",\"type\":\"uint192\"}],\"name\":\"getNonce\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"initCode\",\"type\":\"bytes\"}],\"name\":\"getSenderAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"initCode\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"callGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"verificationGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"preVerificationGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxFeePerGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxPriorityFeePerGas\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"paymasterAndData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"struct UserOperation\",\"name\":\"userOp\",\"type\":\"tuple\"}],\"name\":\"getUserOpHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"initCode\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"callGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"verificationGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"preVerificationGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxFeePerGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxPriorityFeePerGas\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"paymasterAndData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"struct UserOperation[]\",\"name\":\"userOps\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IAggregator\",\"name\":\"aggregator\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"struct IEntryPoint.UserOpsPerAggregator[]\",\"name\":\"opsPerAggregator\",\"type\":\"tuple[]\"},{\"internalType\":\"address payable\",\"name\":\"beneficiary\",\"type\":\"address\"}],\"name\":\"handleAggregatedOps\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"initCode\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"callGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"verificationGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"preVerificationGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxFeePerGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxPriorityFeePerGas\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"paymasterAndData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"struct UserOperation[]\",\"name\":\"ops\",\"type\":\"tuple[]\"},{\"internalType\":\"address payable\",\"name\":\"beneficiary\",\"type\":\"address\"}],\"name\":\"handleOps\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint192\",\"name\":\"key\",\"type\":\"uint192\"}],\"name\":\"incrementNonce\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"},{\"components\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"callGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"verificationGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"preVerificationGas\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"paymaster\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"maxFeePerGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxPriorityFeePerGas\",\"type\":\"uint256\"}],\"internalType\":\"struct EntryPoint.MemoryUserOp\",\"name\":\"mUserOp\",\"type\":\"tuple\"},{\"internalType\":\"bytes32\",\"name\":\"userOpHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"prefund\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"contextOffset\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"preOpGas\",\"type\":\"uint256\"}],\"internalType\":\"struct EntryPoint.UserOpInfo\",\"name\":\"opInfo\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"context\",\"type\":\"bytes\"}],\"name\":\"innerHandleOp\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"actualGasCost\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint192\",\"name\":\"\",\"type\":\"uint192\"}],\"name\":\"nonceSequenceNumber\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"initCode\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"callGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"verificationGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"preVerificationGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxFeePerGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxPriorityFeePerGas\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"paymasterAndData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"struct UserOperation\",\"name\":\"op\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"targetCallData\",\"type\":\"bytes\"}],\"name\":\"simulateHandleOp\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"initCode\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"callGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"verificationGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"preVerificationGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxFeePerGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxPriorityFeePerGas\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"paymasterAndData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"struct UserOperation\",\"name\":\"userOp\",\"type\":\"tuple\"}],\"name\":\"simulateValidation\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unlockStake\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address payable\",\"name\":\"withdrawAddress\",\"type\":\"address\"}],\"name\":\"withdrawStake\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address payable\",\"name\":\"withdrawAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"withdrawAmount\",\"type\":\"uint256\"}],\"name\":\"withdrawTo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]";