diff --git a/Thirdweb.Console/Program.cs b/Thirdweb.Console/Program.cs index 17b41903..4864abef 100644 --- a/Thirdweb.Console/Program.cs +++ b/Thirdweb.Console/Program.cs @@ -43,8 +43,8 @@ #region Indexer -// // Create a ThirdwebInsight instance -// var insight = await ThirdwebInsight.Create(client); +// Create a ThirdwebInsight instance +var insight = await ThirdwebInsight.Create(client); // // Setup some filters // var address = await Utils.GetAddressFromENS(client, "vitalik.eth"); @@ -78,6 +78,17 @@ // ); // Console.WriteLine($"Events: {JsonConvert.SerializeObject(events, Formatting.Indented)}"); +// // Fetch transactions (great amount of optional filters available) +// var transactions = await insight.GetTransactions( +// chainIds: new BigInteger[] { 1 }, // ethereum +// contractAddress: "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d", // bored apes +// 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 transactions +// ); +// Console.WriteLine($"Transactions: {JsonConvert.SerializeObject(transactions, Formatting.Indented)}"); + #endregion #region AI diff --git a/Thirdweb/Thirdweb.Indexer/ThirdwebInsight.Types.cs b/Thirdweb/Thirdweb.Indexer/ThirdwebInsight.Types.cs index d0474b66..a198fc66 100644 --- a/Thirdweb/Thirdweb.Indexer/ThirdwebInsight.Types.cs +++ b/Thirdweb/Thirdweb.Indexer/ThirdwebInsight.Types.cs @@ -37,7 +37,11 @@ public class Token public class Token_ERC20 : Token { } -public class Token_ERC721 : Token { } +public class Token_ERC721 : Token +{ + [JsonProperty("tokenId", Required = Required.Always)] + public BigInteger TokenId { get; set; } +} public class Token_ERC1155 : Token { @@ -81,6 +85,96 @@ public class Event public Decoded Decoded { get; set; } = null!; } +public class Transaction +{ + [JsonProperty("chain_id", Required = Required.Always)] + public BigInteger ChainId { get; set; } + + [JsonProperty("block_number", Required = Required.Always)] + public string BlockNumber { get; set; } = null!; + + [JsonProperty("block_hash", Required = Required.Always)] + public string BlockHash { get; set; } = null!; + + [JsonProperty("block_timestamp", Required = Required.Always)] + public string BlockTimestamp { get; set; } = null!; + + [JsonProperty("hash", Required = Required.Always)] + public string Hash { get; set; } = null!; + + [JsonProperty("nonce", Required = Required.Always)] + public BigInteger Nonce { get; set; } + + [JsonProperty("transaction_index", Required = Required.Always)] + public BigInteger TransactionIndex { get; set; } + + [JsonProperty("from_address", Required = Required.Always)] + public string FromAddress { get; set; } = null!; + + [JsonProperty("to_address", Required = Required.Always)] + public string ToAddress { get; set; } = null!; + + [JsonProperty("value", Required = Required.Always)] + public BigInteger Value { get; set; } + + [JsonProperty("gas_price", Required = Required.Always)] + public BigInteger GasPrice { get; set; } + + [JsonProperty("gas", Required = Required.Always)] + public BigInteger Gas { get; set; } + + [JsonProperty("function_selector", Required = Required.Always)] + public string FunctionSelector { get; set; } = null!; + + [JsonProperty("data", Required = Required.Always)] + public string Data { get; set; } = null!; + + [JsonProperty("max_fee_per_gas", Required = Required.Always)] + public BigInteger MaxFeePerGas { get; set; } + + [JsonProperty("max_priority_fee_per_gas", Required = Required.Always)] + public BigInteger MaxPriorityFeePerGas { get; set; } + + [JsonProperty("transaction_type", Required = Required.Always)] + public BigInteger TransactionType { get; set; } + + [JsonProperty("r", Required = Required.Always)] + public BigInteger R { get; set; } + + [JsonProperty("s", Required = Required.Always)] + public BigInteger S { get; set; } + + [JsonProperty("v", Required = Required.Always)] + public BigInteger V { get; set; } + + [JsonProperty("access_list_json")] + public string AccessListJson { get; set; } + + [JsonProperty("contract_address")] + public string ContractAddress { get; set; } + + [JsonProperty("gas_used")] + public BigInteger? GasUsed { get; set; } + + [JsonProperty("cumulative_gas_used")] + public BigInteger? CumulativeGasUsed { get; set; } + + [JsonProperty("effective_gas_price")] + public BigInteger? EffectiveGasPrice { get; set; } + + [JsonProperty("blob_gas_used")] + public BigInteger? BlobGasUsed { get; set; } + + [JsonProperty("blob_gas_price")] + public BigInteger? BlobGasPrice { get; set; } + + [JsonProperty("logs_bloom")] + public string LogsBloom { get; set; } + + [JsonProperty("status")] + public BigInteger? Status { get; set; } +} + public class Decoded { [JsonProperty("name")] diff --git a/Thirdweb/Thirdweb.Indexer/ThirdwebInsight.cs b/Thirdweb/Thirdweb.Indexer/ThirdwebInsight.cs index 9b32bf1a..5022630d 100644 --- a/Thirdweb/Thirdweb.Indexer/ThirdwebInsight.cs +++ b/Thirdweb/Thirdweb.Indexer/ThirdwebInsight.cs @@ -22,6 +22,12 @@ public class InsightEvents public Meta Meta { get; set; } } +public class InsightTransactions +{ + public Transaction[] Transactions { get; set; } + public Meta Meta { get; set; } +} + public class ThirdwebInsight { private readonly IThirdwebHttpClient _httpClient; @@ -234,6 +240,92 @@ public async Task GetEvents( return new InsightEvents { Events = result.Data, Meta = result.Meta, }; } + /// + /// Get transactions, optionally filtered by contract address, signature, and more. + /// + /// The chain IDs to get the transactions from. + /// The contract address to get the transactions from. (Optional) + /// The signature to filter transactions by. (Optional) + /// The starting block number to get the transactions from. (Optional, if provided, said block is included in query) + /// The ending block number to get the transactions from. (Optional, if provided, said block is included in query) + /// The starting block timestamp to get the transactions from. (Optional, if provided, said block is included in query) + /// The ending block timestamp to get the transactions from. (Optional, if provided, said block is included in query) + /// The field to sort the transactions by. (Default: BlockNumber) + /// The order to sort the transactions by. (Default: Desc) + /// The number of transactions to return. (Default: 20) + /// The page number to return. (Default: 0) + /// Whether to decode the transactions. (Default: true) + /// The transactions and metadata as an instance of . + /// Thrown when a signature is provided without a contract address. + /// /// Thrown when no chain IDs are provided. + public async Task GetTransactions( + BigInteger[] chainIds, + string contractAddress = null, + string signature = 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(signature) && string.IsNullOrEmpty(contractAddress)) + { + throw new ArgumentException("Contract address must be provided when 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/transactions"; + var url = AppendChains( + !string.IsNullOrEmpty(contractAddress) + ? !string.IsNullOrEmpty(signature) + ? $"{baseUrl}/{contractAddress}/{signature}" + : $"{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 InsightTransactions { Transactions = result.Data, Meta = result.Meta, }; + } + private static string AppendChains(string url, BigInteger[] chainIds) { return $"{url}?chain={string.Join("&chain=", chainIds)}";