Skip to content

Commit 652b4b7

Browse files
Nethereum.CoreChain and Nethereum.DevChain
1 parent 81418a5 commit 652b4b7

143 files changed

Lines changed: 14349 additions & 4 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
using Nethereum.ABI.FunctionEncoding.Attributes;
2+
using Nethereum.Contracts;
3+
using System.Numerics;
4+
5+
namespace Nethereum.DevChain.IntegrationDemo.Contracts;
6+
7+
public static class ERC20Contract
8+
{
9+
public const string BYTECODE = "0x608060405234801561000f575f5ffd5b506040518060400160405280600a81526020016926b7b1b5902a37b5b2b760b11b815250604051806040016040528060048152602001634d4f434b60e01b815250816003908161005f919061010c565b50600461006c828261010c565b5050506101c6565b634e487b7160e01b5f52604160045260245ffd5b600181811c9082168061009c57607f821691505b6020821081036100ba57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561010757805f5260205f20601f840160051c810160208510156100e55750805b601f840160051c820191505b81811015610104575f81556001016100f1565b50505b505050565b81516001600160401b0381111561012557610125610074565b610139816101338454610088565b846100c0565b6020601f82116001811461016b575f83156101545750848201515b5f19600385901b1c1916600184901b178455610104565b5f84815260208120601f198516915b8281101561019a578785015182556020948501946001909201910161017a565b50848210156101b757868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b610746806101d35f395ff3fe608060405234801561000f575f5ffd5b506004361061009b575f3560e01c806340c10f191161006357806340c10f191461011457806370a082311461012957806395d89b4114610151578063a9059cbb14610159578063dd62ed3e1461016c575f5ffd5b806306fdde031461009f578063095ea7b3146100bd57806318160ddd146100e057806323b872dd146100f2578063313ce56714610105575b5f5ffd5b6100a76101a4565b6040516100b491906105b6565b60405180910390f35b6100d06100cb366004610606565b610234565b60405190151581526020016100b4565b6002545b6040519081526020016100b4565b6100d061010036600461062e565b61024d565b604051601281526020016100b4565b610127610122366004610606565b610270565b005b6100e4610137366004610668565b6001600160a01b03165f9081526020819052604090205490565b6100a761027e565b6100d0610167366004610606565b61028d565b6100e461017a366004610688565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b6060600380546101b3906106b9565b80601f01602080910402602001604051908101604052809291908181526020018280546101df906106b9565b801561022a5780601f106102015761010080835404028352916020019161022a565b820191905f5260205f20905b81548152906001019060200180831161020d57829003601f168201915b5050505050905090565b5f3361024181858561029a565b60019150505b92915050565b5f3361025a8582856102ac565b61026585858561032d565b506001949350505050565b61027a828261038a565b5050565b6060600480546101b3906106b9565b5f3361024181858561032d565b6102a783838360016103be565b505050565b6001600160a01b038381165f908152600160209081526040808320938616835292905220545f19811015610327578181101561031957604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064015b60405180910390fd5b61032784848484035f6103be565b50505050565b6001600160a01b03831661035657604051634b637e8f60e11b81525f6004820152602401610310565b6001600160a01b03821661037f5760405163ec442f0560e01b81525f6004820152602401610310565b6102a7838383610490565b6001600160a01b0382166103b35760405163ec442f0560e01b81525f6004820152602401610310565b61027a5f8383610490565b6001600160a01b0384166103e75760405163e602df0560e01b81525f6004820152602401610310565b6001600160a01b03831661041057604051634a1406b160e11b81525f6004820152602401610310565b6001600160a01b038085165f908152600160209081526040808320938716835292905220829055801561032757826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161048291815260200190565b60405180910390a350505050565b6001600160a01b0383166104ba578060025f8282546104af91906106f1565b9091555061052a9050565b6001600160a01b0383165f908152602081905260409020548181101561050c5760405163391434e360e21b81526001600160a01b03851660048201526024810182905260448101839052606401610310565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b03821661054657600280548290039055610564565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516105a991815260200190565b60405180910390a3505050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b80356001600160a01b0381168114610601575f5ffd5b919050565b5f5f60408385031215610617575f5ffd5b610620836105eb565b946020939093013593505050565b5f5f5f60608486031215610640575f5ffd5b610649846105eb565b9250610657602085016105eb565b929592945050506040919091013590565b5f60208284031215610678575f5ffd5b610681826105eb565b9392505050565b5f5f60408385031215610699575f5ffd5b6106a2836105eb565b91506106b0602084016105eb565b90509250929050565b600181811c908216806106cd57607f821691505b6020821081036106eb57634e487b7160e01b5f52602260045260245ffd5b50919050565b8082018082111561024757634e487b7160e01b5f52601160045260245ffdfea2646970667358221220f151a46618647e457760c7d59f88e69de993a1735680605d19409339237705ed64736f6c634300081c0033";
10+
11+
public static byte[] GetDeploymentBytecode()
12+
{
13+
return Nethereum.Hex.HexConvertors.Extensions.HexByteConvertorExtensions.HexToByteArray(BYTECODE);
14+
}
15+
}
16+
17+
[Function("balanceOf", "uint256")]
18+
public class BalanceOfFunction : FunctionMessage
19+
{
20+
[Parameter("address", "account", 1)]
21+
public virtual string Account { get; set; } = string.Empty;
22+
}
23+
24+
[Function("totalSupply", "uint256")]
25+
public class TotalSupplyFunction : FunctionMessage
26+
{
27+
}
28+
29+
[Function("name", "string")]
30+
public class NameFunction : FunctionMessage
31+
{
32+
}
33+
34+
[Function("symbol", "string")]
35+
public class SymbolFunction : FunctionMessage
36+
{
37+
}
38+
39+
[Function("decimals", "uint8")]
40+
public class DecimalsFunction : FunctionMessage
41+
{
42+
}
43+
44+
[Function("transfer", "bool")]
45+
public class TransferFunction : FunctionMessage
46+
{
47+
[Parameter("address", "to", 1)]
48+
public virtual string To { get; set; } = string.Empty;
49+
[Parameter("uint256", "value", 2)]
50+
public virtual BigInteger Value { get; set; }
51+
}
52+
53+
[Function("approve", "bool")]
54+
public class ApproveFunction : FunctionMessage
55+
{
56+
[Parameter("address", "spender", 1)]
57+
public virtual string Spender { get; set; } = string.Empty;
58+
[Parameter("uint256", "value", 2)]
59+
public virtual BigInteger Value { get; set; }
60+
}
61+
62+
[Function("mint")]
63+
public class MintFunction : FunctionMessage
64+
{
65+
[Parameter("address", "to", 1)]
66+
public virtual string To { get; set; } = string.Empty;
67+
[Parameter("uint256", "amount", 2)]
68+
public virtual BigInteger Amount { get; set; }
69+
}
70+
71+
[Event("Transfer")]
72+
public class TransferEventDTO : IEventDTO
73+
{
74+
[Parameter("address", "from", 1, true)]
75+
public virtual string From { get; set; } = string.Empty;
76+
[Parameter("address", "to", 2, true)]
77+
public virtual string To { get; set; } = string.Empty;
78+
[Parameter("uint256", "value", 3, false)]
79+
public virtual BigInteger Value { get; set; }
80+
}
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
using System.Text.Json;
2+
using Microsoft.AspNetCore.Builder;
3+
using Microsoft.AspNetCore.Http;
4+
using Microsoft.Extensions.DependencyInjection;
5+
using Microsoft.Extensions.Logging;
6+
using Nethereum.CoreChain.Rpc;
7+
using Nethereum.DevChain;
8+
using Nethereum.DevChain.Server.Accounts;
9+
using Nethereum.DevChain.Server.Configuration;
10+
using Nethereum.DevChain.Server.Server;
11+
using Nethereum.JsonRpc.Client.RpcMessages;
12+
13+
namespace Nethereum.DevChain.IntegrationDemo.Helpers;
14+
15+
public class DevChainTestServer : IAsyncDisposable
16+
{
17+
private WebApplication? _app;
18+
private DevChainNode? _node;
19+
private DevAccountManager? _accountManager;
20+
private Task? _runTask;
21+
private CancellationTokenSource? _cts;
22+
23+
public int Port { get; private set; }
24+
public string Url => $"http://127.0.0.1:{Port}";
25+
public DevChainNode? Node => _node;
26+
public DevAccountManager? AccountManager => _accountManager;
27+
28+
public async Task StartAsync(DevChainServerConfig? config = null)
29+
{
30+
config ??= new DevChainServerConfig();
31+
Port = config.Port;
32+
33+
var builder = WebApplication.CreateBuilder();
34+
builder.Services.AddDevChainServer(config);
35+
builder.Services.AddLogging(logging =>
36+
{
37+
logging.SetMinimumLevel(LogLevel.Warning);
38+
});
39+
40+
_app = builder.Build();
41+
_node = _app.Services.GetRequiredService<DevChainNode>();
42+
_accountManager = _app.Services.GetRequiredService<DevAccountManager>();
43+
var dispatcher = _app.Services.GetRequiredService<RpcDispatcher>();
44+
45+
await _node.StartAsync(_accountManager.Accounts.Select(a => a.Address));
46+
47+
_app.MapPost("/", async (HttpContext httpContext) =>
48+
{
49+
try
50+
{
51+
using var reader = new StreamReader(httpContext.Request.Body);
52+
var json = await reader.ReadToEndAsync();
53+
54+
if (string.IsNullOrWhiteSpace(json))
55+
{
56+
httpContext.Response.StatusCode = 400;
57+
await httpContext.Response.WriteAsJsonAsync(new { error = "Empty request body" });
58+
return;
59+
}
60+
61+
var jsonOptions = new JsonSerializerOptions
62+
{
63+
PropertyNameCaseInsensitive = true
64+
};
65+
66+
if (json.TrimStart().StartsWith('['))
67+
{
68+
var requests = JsonSerializer.Deserialize<RpcRequestMessage[]>(json, jsonOptions);
69+
if (requests != null)
70+
{
71+
var responses = await dispatcher.DispatchBatchAsync(requests);
72+
httpContext.Response.ContentType = "application/json";
73+
await httpContext.Response.WriteAsync(JsonSerializer.Serialize(responses));
74+
return;
75+
}
76+
}
77+
78+
var request = JsonSerializer.Deserialize<RpcRequestMessage>(json, jsonOptions);
79+
if (request == null)
80+
{
81+
httpContext.Response.StatusCode = 400;
82+
await httpContext.Response.WriteAsJsonAsync(new { error = "Invalid JSON-RPC request" });
83+
return;
84+
}
85+
86+
var response = await dispatcher.DispatchAsync(request);
87+
httpContext.Response.ContentType = "application/json";
88+
await httpContext.Response.WriteAsync(JsonSerializer.Serialize(response));
89+
}
90+
catch (JsonException)
91+
{
92+
httpContext.Response.StatusCode = 400;
93+
httpContext.Response.ContentType = "application/json";
94+
var errorResponse = new { jsonrpc = "2.0", error = new { code = -32700, message = "Parse error" }, id = (object?)null };
95+
await httpContext.Response.WriteAsync(JsonSerializer.Serialize(errorResponse));
96+
}
97+
catch (Exception ex)
98+
{
99+
httpContext.Response.StatusCode = 500;
100+
httpContext.Response.ContentType = "application/json";
101+
var errorResponse = new { jsonrpc = "2.0", error = new { code = -32603, message = "Internal error: " + ex.Message }, id = (object?)null };
102+
await httpContext.Response.WriteAsync(JsonSerializer.Serialize(errorResponse));
103+
}
104+
});
105+
106+
_app.MapGet("/", () => Results.Ok(new { status = "ok" }));
107+
108+
_cts = new CancellationTokenSource();
109+
110+
_runTask = Task.Run(async () =>
111+
{
112+
try
113+
{
114+
await _app.RunAsync($"http://127.0.0.1:{Port}");
115+
}
116+
catch (OperationCanceledException)
117+
{
118+
}
119+
});
120+
121+
await WaitForServerReadyAsync();
122+
}
123+
124+
private async Task WaitForServerReadyAsync(int maxRetries = 50)
125+
{
126+
using var client = new HttpClient();
127+
for (int i = 0; i < maxRetries; i++)
128+
{
129+
try
130+
{
131+
var response = await client.GetAsync(Url);
132+
if (response.IsSuccessStatusCode)
133+
return;
134+
}
135+
catch
136+
{
137+
}
138+
await Task.Delay(100);
139+
}
140+
throw new Exception($"Server did not become ready at {Url}");
141+
}
142+
143+
public async ValueTask DisposeAsync()
144+
{
145+
if (_cts != null)
146+
{
147+
await _cts.CancelAsync();
148+
_cts.Dispose();
149+
}
150+
151+
if (_app != null)
152+
{
153+
await _app.StopAsync();
154+
await _app.DisposeAsync();
155+
}
156+
157+
if (_runTask != null)
158+
{
159+
try
160+
{
161+
await _runTask;
162+
}
163+
catch (OperationCanceledException)
164+
{
165+
}
166+
}
167+
}
168+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System.Numerics;
2+
3+
namespace Nethereum.DevChain.IntegrationDemo.Helpers;
4+
5+
public static class TestConfiguration
6+
{
7+
public const string DefaultPrivateKey = "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
8+
public const string DefaultAddress = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266";
9+
public const int ChainId = 31337;
10+
public const int DefaultPort = 8545;
11+
12+
public static readonly BigInteger InitialBalance = BigInteger.Parse("10000000000000000000000");
13+
14+
public const string MainnetRpcUrl = "https://eth.llamarpc.com";
15+
public const string SepoliaRpcUrl = "https://sepolia.drpc.org";
16+
17+
public const string UsdcContractAddress = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
18+
public const string DaiContractAddress = "0x6B175474E89094C44Da98b954EescdECF8e0E9Fa";
19+
20+
public static string SecondaryAddress => "0x70997970C51812dc3A010C7d01b50e0d17dc79C8";
21+
public static string SecondaryPrivateKey => "59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d";
22+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<ProjectReference Include="..\..\src\Nethereum.DevChain.Server\Nethereum.DevChain.Server.csproj" />
12+
<ProjectReference Include="..\..\src\Nethereum.Web3\Nethereum.Web3.csproj" />
13+
<ProjectReference Include="..\..\src\Nethereum.Accounts\Nethereum.Accounts.csproj" />
14+
<ProjectReference Include="..\..\src\Nethereum.Contracts\Nethereum.Contracts.csproj" />
15+
</ItemGroup>
16+
17+
</Project>
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using Nethereum.DevChain.IntegrationDemo.Tests;
2+
3+
Console.WriteLine();
4+
Console.ForegroundColor = ConsoleColor.Cyan;
5+
Console.WriteLine(@" _ _ _ _");
6+
Console.WriteLine(@"| \ | | ___| |_| |__ ___ _ __ ___ _ _ _ __ ___");
7+
Console.WriteLine(@"| \| |/ _ \ __| '_ \ / _ \ '__/ _ \ | | | '_ ` _ \");
8+
Console.WriteLine(@"| |\ | __/ |_| | | | __/ | | __/ |_| | | | | | |");
9+
Console.WriteLine(@"|_| \_|\___|\__|_| |_|\___|_| \___|\__,_|_| |_| |_|");
10+
Console.WriteLine();
11+
Console.ForegroundColor = ConsoleColor.Yellow;
12+
Console.WriteLine(" DevChain Integration Demo v1.0.0");
13+
Console.ResetColor();
14+
Console.WriteLine();
15+
Console.WriteLine("============================================");
16+
Console.WriteLine();
17+
18+
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
19+
20+
try
21+
{
22+
await ServerConnectionTests.RunAsync();
23+
await ContractDeploymentTests.RunAsync();
24+
await ContractInteractionTests.RunAsync();
25+
await ForkingTests.RunAsync();
26+
27+
stopwatch.Stop();
28+
29+
Console.ForegroundColor = ConsoleColor.Green;
30+
Console.WriteLine("============================================");
31+
Console.WriteLine($"All Tests Completed Successfully!");
32+
Console.WriteLine($"Total Time: {stopwatch.ElapsedMilliseconds}ms");
33+
Console.WriteLine("============================================");
34+
Console.ResetColor();
35+
}
36+
catch (Exception ex)
37+
{
38+
stopwatch.Stop();
39+
40+
Console.ForegroundColor = ConsoleColor.Red;
41+
Console.WriteLine("============================================");
42+
Console.WriteLine("TEST FAILED!");
43+
Console.WriteLine($"Error: {ex.Message}");
44+
Console.WriteLine("============================================");
45+
Console.ResetColor();
46+
Console.WriteLine();
47+
Console.WriteLine("Stack Trace:");
48+
Console.WriteLine(ex.StackTrace);
49+
Environment.Exit(1);
50+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using Nethereum.DevChain.IntegrationDemo.Contracts;
2+
using Nethereum.DevChain.IntegrationDemo.Helpers;
3+
using Nethereum.DevChain.Server.Configuration;
4+
using Nethereum.Hex.HexTypes;
5+
6+
namespace Nethereum.DevChain.IntegrationDemo.Tests;
7+
8+
public static class ContractDeploymentTests
9+
{
10+
public static async Task RunAsync()
11+
{
12+
Console.WriteLine("=== Contract Deployment Tests ===");
13+
Console.WriteLine();
14+
15+
await TestERC20DeploymentAsync();
16+
17+
Console.WriteLine("Contract Deployment Tests: PASSED");
18+
Console.WriteLine();
19+
}
20+
21+
private static async Task TestERC20DeploymentAsync()
22+
{
23+
Console.Write(" Deploying ERC20 contract... ");
24+
25+
await using var server = new DevChainTestServer();
26+
await server.StartAsync(new DevChainServerConfig
27+
{
28+
Port = 8548,
29+
ChainId = TestConfiguration.ChainId
30+
});
31+
32+
var account = new Nethereum.Web3.Accounts.Account(TestConfiguration.DefaultPrivateKey, TestConfiguration.ChainId);
33+
var web3 = new Nethereum.Web3.Web3(account, server.Url);
34+
35+
var receipt = await web3.Eth.DeployContract.SendRequestAndWaitForReceiptAsync(
36+
ERC20Contract.BYTECODE,
37+
account.Address,
38+
new HexBigInteger(3000000)
39+
);
40+
41+
if (string.IsNullOrEmpty(receipt.ContractAddress))
42+
throw new Exception("Contract address not returned");
43+
44+
var code = await web3.Eth.GetCode.SendRequestAsync(receipt.ContractAddress);
45+
if (code == null || code.Length < 10)
46+
throw new Exception("No code at contract address");
47+
48+
Console.WriteLine($"OK (Address: {receipt.ContractAddress})");
49+
Console.WriteLine($" Gas Used: {receipt.GasUsed.Value}");
50+
Console.WriteLine($" Tx Hash: {receipt.TransactionHash}");
51+
}
52+
}

0 commit comments

Comments
 (0)