Skip to content

Commit

Permalink
Nethereum.DataServices.Etherscan Etherscan api integration
Browse files Browse the repository at this point in the history
  • Loading branch information
juanfranblanco committed Jul 27, 2023
1 parent 2377013 commit 226371c
Show file tree
Hide file tree
Showing 14 changed files with 670 additions and 0 deletions.
38 changes: 38 additions & 0 deletions Nethereum.sln
Expand Up @@ -254,6 +254,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethereum.EVM.Contracts", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethereum.RPC.Extensions", "src\Nethereum.RPC.Extensions\Nethereum.RPC.Extensions.csproj", "{DC1269E2-E56A-4D8D-BDD8-F6B67D522194}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethereum.DataServices.Etherscan", "src\Nethereum.DataServices.Etherscan\Nethereum.DataServices.Etherscan.csproj", "{B317D562-ABFF-4A1C-AE84-7E65EB16F80D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethereum.DataServices.Etherscan.IntegrationTests", "tests\Nethereum.DataServices.Etherscan.IntegrationTests\Nethereum.DataServices.Etherscan.IntegrationTests.csproj", "{81CCC4DA-CA23-4B37-A68C-165F0007F541}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -1658,6 +1662,38 @@ Global
{DC1269E2-E56A-4D8D-BDD8-F6B67D522194}.Release|x64.Build.0 = Release|Any CPU
{DC1269E2-E56A-4D8D-BDD8-F6B67D522194}.Release|x86.ActiveCfg = Release|Any CPU
{DC1269E2-E56A-4D8D-BDD8-F6B67D522194}.Release|x86.Build.0 = Release|Any CPU
{B317D562-ABFF-4A1C-AE84-7E65EB16F80D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B317D562-ABFF-4A1C-AE84-7E65EB16F80D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B317D562-ABFF-4A1C-AE84-7E65EB16F80D}.Debug|ARM.ActiveCfg = Debug|Any CPU
{B317D562-ABFF-4A1C-AE84-7E65EB16F80D}.Debug|ARM.Build.0 = Debug|Any CPU
{B317D562-ABFF-4A1C-AE84-7E65EB16F80D}.Debug|x64.ActiveCfg = Debug|Any CPU
{B317D562-ABFF-4A1C-AE84-7E65EB16F80D}.Debug|x64.Build.0 = Debug|Any CPU
{B317D562-ABFF-4A1C-AE84-7E65EB16F80D}.Debug|x86.ActiveCfg = Debug|Any CPU
{B317D562-ABFF-4A1C-AE84-7E65EB16F80D}.Debug|x86.Build.0 = Debug|Any CPU
{B317D562-ABFF-4A1C-AE84-7E65EB16F80D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B317D562-ABFF-4A1C-AE84-7E65EB16F80D}.Release|Any CPU.Build.0 = Release|Any CPU
{B317D562-ABFF-4A1C-AE84-7E65EB16F80D}.Release|ARM.ActiveCfg = Release|Any CPU
{B317D562-ABFF-4A1C-AE84-7E65EB16F80D}.Release|ARM.Build.0 = Release|Any CPU
{B317D562-ABFF-4A1C-AE84-7E65EB16F80D}.Release|x64.ActiveCfg = Release|Any CPU
{B317D562-ABFF-4A1C-AE84-7E65EB16F80D}.Release|x64.Build.0 = Release|Any CPU
{B317D562-ABFF-4A1C-AE84-7E65EB16F80D}.Release|x86.ActiveCfg = Release|Any CPU
{B317D562-ABFF-4A1C-AE84-7E65EB16F80D}.Release|x86.Build.0 = Release|Any CPU
{81CCC4DA-CA23-4B37-A68C-165F0007F541}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{81CCC4DA-CA23-4B37-A68C-165F0007F541}.Debug|Any CPU.Build.0 = Debug|Any CPU
{81CCC4DA-CA23-4B37-A68C-165F0007F541}.Debug|ARM.ActiveCfg = Debug|Any CPU
{81CCC4DA-CA23-4B37-A68C-165F0007F541}.Debug|ARM.Build.0 = Debug|Any CPU
{81CCC4DA-CA23-4B37-A68C-165F0007F541}.Debug|x64.ActiveCfg = Debug|Any CPU
{81CCC4DA-CA23-4B37-A68C-165F0007F541}.Debug|x64.Build.0 = Debug|Any CPU
{81CCC4DA-CA23-4B37-A68C-165F0007F541}.Debug|x86.ActiveCfg = Debug|Any CPU
{81CCC4DA-CA23-4B37-A68C-165F0007F541}.Debug|x86.Build.0 = Debug|Any CPU
{81CCC4DA-CA23-4B37-A68C-165F0007F541}.Release|Any CPU.ActiveCfg = Release|Any CPU
{81CCC4DA-CA23-4B37-A68C-165F0007F541}.Release|Any CPU.Build.0 = Release|Any CPU
{81CCC4DA-CA23-4B37-A68C-165F0007F541}.Release|ARM.ActiveCfg = Release|Any CPU
{81CCC4DA-CA23-4B37-A68C-165F0007F541}.Release|ARM.Build.0 = Release|Any CPU
{81CCC4DA-CA23-4B37-A68C-165F0007F541}.Release|x64.ActiveCfg = Release|Any CPU
{81CCC4DA-CA23-4B37-A68C-165F0007F541}.Release|x64.Build.0 = Release|Any CPU
{81CCC4DA-CA23-4B37-A68C-165F0007F541}.Release|x86.ActiveCfg = Release|Any CPU
{81CCC4DA-CA23-4B37-A68C-165F0007F541}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1786,6 +1822,8 @@ Global
{F73B9EBD-7C78-4A2E-8058-09F255FBAED1} = {962EC435-6130-4B75-BAC1-12323B067443}
{B00B0908-B55D-4F9D-BD8D-70F81DDBA2F4} = {8F18B64A-CF6B-454F-A19C-8CE8FBFE2E58}
{DC1269E2-E56A-4D8D-BDD8-F6B67D522194} = {F7E94706-FF67-4766-BBBD-27E7CED957A3}
{B317D562-ABFF-4A1C-AE84-7E65EB16F80D} = {F73B9EBD-7C78-4A2E-8058-09F255FBAED1}
{81CCC4DA-CA23-4B37-A68C-165F0007F541} = {F73B9EBD-7C78-4A2E-8058-09F255FBAED1}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {07A44726-8749-4A9A-8079-FA3C3213BDC1}
Expand Down
@@ -0,0 +1,41 @@
using Nethereum.DataServices.Etherscan.Responses;
using System.Collections.Generic;
using System.Numerics;
using System.Threading.Tasks;

namespace Nethereum.DataServices.Etherscan
{
public class EtherscanApiAccountsService
{

public EtherscanRequestService EtherscanRequestService { get; private set; }

public EtherscanApiAccountsService(EtherscanRequestService etherscanRequestService)
{
EtherscanRequestService = etherscanRequestService;
}

public Task<EtherscanResponse<List<EtherscanGetAccountTransactionsResponse>>> GetAccountTransactionsAsync(string address, int page = 1, int offset = 10, EtherscanResultSort sort = Etherscan.EtherscanResultSort.Ascending)
{
return GetAccountTransactionsAsync(address, 0, BigInteger.Parse("999999999999999"), page, offset, sort);
}


public async Task<EtherscanResponse<List<EtherscanGetAccountTransactionsResponse>>> GetAccountTransactionsAsync(string address, BigInteger startBlock, BigInteger endBlock, int page = 1, int offset = 10, EtherscanResultSort sort = Etherscan.EtherscanResultSort.Ascending)
{
var url = $"{EtherscanRequestService.BaseUrl}api?module=account&action=txlist&address={address}&startblock={startBlock}&endblock{endBlock}&page={page}&offset={offset}&sort={sort.ConvertToRequestFormattedString()}&apikey={EtherscanRequestService.ApiKey}";
return await EtherscanRequestService.GetDataAsync<List<EtherscanGetAccountTransactionsResponse>>(url).ConfigureAwait(false);
}

public Task<EtherscanResponse<List<EtherscanGetAccountInternalTransactionsResponse>>> GetAccountInternalTransactionsAsync(string address, int page = 1, int offset = 10, EtherscanResultSort sort = Etherscan.EtherscanResultSort.Ascending)
{
return GetAccountInternalTransactionsAsync(address, 0, BigInteger.Parse("999999999999999"), page, offset, sort);
}

public async Task<EtherscanResponse<List<EtherscanGetAccountInternalTransactionsResponse>>> GetAccountInternalTransactionsAsync(string address, BigInteger startBlock, BigInteger endBlock, int page = 1, int offset = 10, EtherscanResultSort sort = Etherscan.EtherscanResultSort.Ascending)
{
var url = $"{EtherscanRequestService.BaseUrl}api?module=account&action=txlistinternal&address={address}&startblock={startBlock}&endblock{endBlock}&page={page}&offset={offset}&sort={sort.ConvertToRequestFormattedString()}&apikey={EtherscanRequestService.ApiKey}";
return await EtherscanRequestService.GetDataAsync<List<EtherscanGetAccountInternalTransactionsResponse>>(url).ConfigureAwait(false);
}
}
}
@@ -0,0 +1,38 @@
using Nethereum.DataServices.Etherscan.Responses;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;

namespace Nethereum.DataServices.Etherscan
{


public class EtherscanApiContractsService
{
public EtherscanRequestService EtherscanRequestService { get; private set; }

public EtherscanApiContractsService(EtherscanRequestService etherscanRequestService)
{
EtherscanRequestService = etherscanRequestService;
}

public async Task<EtherscanResponse<List<EtherscanGetSourceCodeResponse>>> GetSourceCodeAsync(string address)
{
var url = $"{EtherscanRequestService.BaseUrl}api?module=contract&action=getsourcecode&address={address}&apikey={EtherscanRequestService.ApiKey}";
return await EtherscanRequestService.GetDataAsync<List<EtherscanGetSourceCodeResponse>>(url).ConfigureAwait(false);
}

public async Task<EtherscanResponse<string>> GetAbiAsync(string address)
{
var url = $"{EtherscanRequestService.BaseUrl}api?module=contract&action=getabi&address={address}&apikey={EtherscanRequestService.ApiKey}";
return await EtherscanRequestService.GetDataAsync<string>(url).ConfigureAwait(false);
}

public async Task<EtherscanResponse<List<EtherscanGetContractCreatorResponse>>> GetContractCreatorAndCreationTxHashAsync(params string[] addresses)
{
var url = $"{EtherscanRequestService.BaseUrl}api?module=contract&action=getcontractcreation&contractaddresses={string.Join(",",addresses)}&apikey={EtherscanRequestService.ApiKey}";
return await EtherscanRequestService.GetDataAsync<List<EtherscanGetContractCreatorResponse>>(url).ConfigureAwait(false);
}
}
}
45 changes: 45 additions & 0 deletions src/Nethereum.DataServices.Etherscan/EtherscanApiService.cs
@@ -0,0 +1,45 @@
using Nethereum.DataServices.Etherscan.Responses;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;

namespace Nethereum.DataServices.Etherscan
{
public class EtherscanApiService
{
private EtherscanRequestService requestService;
public EtherscanApiContractsService Contracts { get; private set; }
public EtherscanApiAccountsService Accounts { get; private set; }
public EtherscanApiService(HttpClient httpClient, string baseUrl, string apiKey = EtherscanRequestService.DefaultToken):this(new EtherscanRequestService(httpClient, baseUrl, apiKey))
{

}

public EtherscanApiService(HttpClient httpClient, EtherscanChain chain, string apiKey = EtherscanRequestService.DefaultToken):
this(new EtherscanRequestService(httpClient, chain, apiKey))
{

}

public EtherscanApiService(EtherscanChain chain = EtherscanChain.Mainnet, string apiKey = EtherscanRequestService.DefaultToken)
:this(new EtherscanRequestService(chain, apiKey))
{

}

public EtherscanApiService(EtherscanRequestService etherscanRequestService)
{
requestService = etherscanRequestService;
InitialiseServices();
}

private void InitialiseServices()
{
Contracts = new EtherscanApiContractsService(requestService);
Accounts = new EtherscanApiAccountsService(requestService);
}
}
}
32 changes: 32 additions & 0 deletions src/Nethereum.DataServices.Etherscan/EtherscanChain.cs
@@ -0,0 +1,32 @@
using System;

namespace Nethereum.DataServices.Etherscan
{
public enum EtherscanChain
{
Mainnet,

}

public enum EtherscanResultSort
{
Ascending,
Descending
}

public static class EtherscanExtensions
{
public static string ConvertToRequestFormattedString(this EtherscanResultSort value)
{
switch (value)
{
case EtherscanResultSort.Ascending:
return "asc";
case EtherscanResultSort.Descending:
return "desc";
}

throw new NotImplementedException();
}
}
}
66 changes: 66 additions & 0 deletions src/Nethereum.DataServices.Etherscan/EtherscanRequestService.cs
@@ -0,0 +1,66 @@
using Nethereum.DataServices.Etherscan.Responses;
using Newtonsoft.Json;
using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;

namespace Nethereum.DataServices.Etherscan
{
public class EtherscanRequestService
{
public const string DefaultToken = "YourApiKeyToken";

public static string GetBaseUrl(EtherscanChain chain)
{
switch (chain)
{
case EtherscanChain.Mainnet:
return "https://api.etherscan.io/";
}
throw new NotImplementedException();
}

public EtherscanRequestService(HttpClient httpClient, string baseUrl, string apiKey= DefaultToken)
{
HttpClient = httpClient;
BaseUrl = baseUrl;
ApiKey = apiKey;
}

public EtherscanRequestService(HttpClient httpClient, EtherscanChain chain, string apiKey = DefaultToken)
{
HttpClient = httpClient;
BaseUrl = GetBaseUrl(chain);
ApiKey = apiKey;
}

public EtherscanRequestService(EtherscanChain chain = EtherscanChain.Mainnet, string apiKey = DefaultToken)
{
HttpClient = new HttpClient();
BaseUrl = GetBaseUrl(chain);
ApiKey = apiKey;
}

public HttpClient HttpClient { get; }
public string BaseUrl { get; }
public string ApiKey { get; }



public async Task<EtherscanResponse<T>> GetDataAsync<T>(string url)
{
var httpResponseMessage = await HttpClient.GetAsync(url).ConfigureAwait(false);
httpResponseMessage.EnsureSuccessStatusCode();
var stream = await httpResponseMessage.Content.ReadAsStreamAsync().ConfigureAwait(false);
using (var streamReader = new StreamReader(stream))
using (var reader = new JsonTextReader(streamReader))
{
var serializer = JsonSerializer.Create();
var message = serializer.Deserialize<EtherscanResponse<T>>(reader);

return message;
}
}
}
}
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
<PropertyGroup>
<Description>Nethereum DataServices Etherscan library, provides client access to Etherscan rest apis</Description>
<AssemblyTitle>Nethereum.DataServices.Etherscan</AssemblyTitle>
<VersionPrefix>$(NethereumVersion)</VersionPrefix>
<TargetFrameworks>$(DefaultFrameworks)</TargetFrameworks>
<AssemblyName>Nethereum.DataServices.Etherscan</AssemblyName>
<PackageId>Nethereum.DataServices.Etherscan</PackageId>
<PackageTags>Netherum;Ethereum;Blockchain;Etherscan</PackageTags>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
</PropertyGroup>


<PropertyGroup Condition=" '$(TargetFramework)' != 'net35' And '$(TargetUnityNet461AOT)' != 'true'">
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>..\..\NethereumKey.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' != 'net35' And '$(TargetUnityNet461AOT)' != 'true'">
<None Include="..\..\NethereumKey.snk" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net451' Or '$(TargetFramework)' == 'net461'">
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Nethereum.ABI\Nethereum.ABI.csproj" />
</ItemGroup>


</Project>
@@ -0,0 +1,50 @@
using Newtonsoft.Json;

namespace Nethereum.DataServices.Etherscan.Responses
{
public class EtherscanGetAccountInternalTransactionsResponse
{
[JsonProperty("blockNumber")]
public string BlockNumber { get; set; }

[JsonProperty("timeStamp")]
public string TimeStamp { get; set; }

[JsonProperty("hash")]
public string Hash { get; set; }

[JsonProperty("from")]
public string From { get; set; }

[JsonProperty("to")]
public string To { get; set; }

[JsonProperty("value")]
public string Value { get; set; }

[JsonProperty("contractAddress")]
public string ContractAddress { get; set; }

[JsonProperty("input")]
public string Input { get; set; }

[JsonProperty("type")]
public string Type { get; set; }

[JsonProperty("gas")]
public string Gas { get; set; }

[JsonProperty("gasUsed")]
public string GasUsed { get; set; }

[JsonProperty("traceId")]
public string TraceId { get; set; }

[JsonProperty("isError")]
public string IsError { get; set; }

[JsonProperty("errCode")]
public string ErrCode { get; set; }
}

}

0 comments on commit 226371c

Please sign in to comment.