2025-07-30 09:04:07 +02:00
|
|
|
|
using System.Numerics;
|
|
|
|
|
|
using BlockchainUtils;
|
2024-12-17 15:54:52 +01:00
|
|
|
|
using Logging;
|
2023-12-19 15:30:46 +01:00
|
|
|
|
using Nethereum.ABI.FunctionEncoding.Attributes;
|
2023-04-18 13:22:41 +02:00
|
|
|
|
using Nethereum.Contracts;
|
2023-09-19 11:51:59 +02:00
|
|
|
|
using Nethereum.RPC.Eth.DTOs;
|
2023-04-14 10:51:35 +02:00
|
|
|
|
using Nethereum.Web3;
|
|
|
|
|
|
using Utils;
|
|
|
|
|
|
|
|
|
|
|
|
namespace NethereumWorkflow
|
|
|
|
|
|
{
|
2023-04-14 12:37:05 +02:00
|
|
|
|
public class NethereumInteraction
|
2023-04-14 10:51:35 +02:00
|
|
|
|
{
|
2024-12-17 16:06:06 +01:00
|
|
|
|
private readonly BlockCache blockCache;
|
2024-03-21 16:26:48 +01:00
|
|
|
|
|
2023-09-15 16:27:08 +02:00
|
|
|
|
private readonly ILog log;
|
2023-04-14 10:51:35 +02:00
|
|
|
|
private readonly Web3 web3;
|
|
|
|
|
|
|
2024-12-17 16:06:06 +01:00
|
|
|
|
internal NethereumInteraction(ILog log, Web3 web3, BlockCache blockCache)
|
2023-04-14 10:51:35 +02:00
|
|
|
|
{
|
2023-04-25 11:31:15 +02:00
|
|
|
|
this.log = log;
|
2023-04-14 10:51:35 +02:00
|
|
|
|
this.web3 = web3;
|
2024-12-17 16:06:06 +01:00
|
|
|
|
this.blockCache = blockCache;
|
2023-04-14 10:51:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-30 09:04:07 +02:00
|
|
|
|
|
|
|
|
|
|
public string SendEth(string toAddress, BigInteger ethAmount)
|
2023-09-19 11:51:59 +02:00
|
|
|
|
{
|
2023-10-23 16:04:50 +02:00
|
|
|
|
log.Debug();
|
2025-07-30 09:04:07 +02:00
|
|
|
|
var receipt = Time.Wait(web3.Eth.GetEtherTransferService().TransferEtherAndWaitForReceiptAsync(toAddress, ((decimal)ethAmount)));
|
2023-09-19 11:51:59 +02:00
|
|
|
|
if (!receipt.Succeeded()) throw new Exception("Unable to send Eth");
|
2023-12-15 11:02:06 +01:00
|
|
|
|
return receipt.TransactionHash;
|
2023-09-19 11:51:59 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-30 09:04:07 +02:00
|
|
|
|
public BigInteger GetEthBalance()
|
2023-09-19 11:51:59 +02:00
|
|
|
|
{
|
2023-10-23 16:04:50 +02:00
|
|
|
|
log.Debug();
|
2023-09-19 11:51:59 +02:00
|
|
|
|
return GetEthBalance(web3.TransactionManager.Account.Address);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-30 09:04:07 +02:00
|
|
|
|
public BigInteger GetEthBalance(string address)
|
2023-09-19 11:51:59 +02:00
|
|
|
|
{
|
2023-10-23 16:04:50 +02:00
|
|
|
|
log.Debug();
|
2023-09-19 11:51:59 +02:00
|
|
|
|
var balance = Time.Wait(web3.Eth.GetBalance.SendRequestAsync(address));
|
2025-07-30 09:04:07 +02:00
|
|
|
|
return balance.Value;
|
2023-09-19 11:51:59 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-09-19 13:39:24 +02:00
|
|
|
|
public TResult Call<TFunction, TResult>(string contractAddress, TFunction function) where TFunction : FunctionMessage, new()
|
2023-04-18 13:22:41 +02:00
|
|
|
|
{
|
2023-10-23 16:04:50 +02:00
|
|
|
|
log.Debug(typeof(TFunction).ToString());
|
2023-09-19 13:39:24 +02:00
|
|
|
|
var handler = web3.Eth.GetContractQueryHandler<TFunction>();
|
|
|
|
|
|
return Time.Wait(handler.QueryAsync<TResult>(contractAddress, function));
|
2023-04-25 07:46:09 +02:00
|
|
|
|
}
|
2023-04-24 14:09:23 +02:00
|
|
|
|
|
2025-03-04 15:24:25 +01:00
|
|
|
|
public TResult Call<TFunction, TResult>(string contractAddress, TFunction function, ulong blockNumber) where TFunction : FunctionMessage, new()
|
|
|
|
|
|
{
|
|
|
|
|
|
log.Debug(typeof(TFunction).ToString());
|
|
|
|
|
|
var handler = web3.Eth.GetContractQueryHandler<TFunction>();
|
|
|
|
|
|
return Time.Wait(handler.QueryAsync<TResult>(contractAddress, function, new BlockParameter(blockNumber)));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-30 09:04:07 +02:00
|
|
|
|
public void Call<TFunction>(string contractAddress, TFunction function) where TFunction : FunctionMessage, new()
|
|
|
|
|
|
{
|
|
|
|
|
|
log.Debug(typeof(TFunction).ToString());
|
|
|
|
|
|
var handler = web3.Eth.GetContractQueryHandler<TFunction>();
|
|
|
|
|
|
Time.Wait(handler.QueryRawAsync(contractAddress, function));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-04 15:24:25 +01:00
|
|
|
|
public void Call<TFunction>(string contractAddress, TFunction function, ulong blockNumber) where TFunction : FunctionMessage, new()
|
|
|
|
|
|
{
|
|
|
|
|
|
log.Debug(typeof(TFunction).ToString());
|
|
|
|
|
|
var handler = web3.Eth.GetContractQueryHandler<TFunction>();
|
|
|
|
|
|
var result = Time.Wait(handler.QueryRawAsync(contractAddress, function, new BlockParameter(blockNumber)));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-15 11:02:06 +01:00
|
|
|
|
public string SendTransaction<TFunction>(string contractAddress, TFunction function) where TFunction : FunctionMessage, new()
|
2023-04-25 07:46:09 +02:00
|
|
|
|
{
|
2023-10-23 16:04:50 +02:00
|
|
|
|
log.Debug();
|
2023-09-19 13:39:24 +02:00
|
|
|
|
var handler = web3.Eth.GetContractTransactionHandler<TFunction>();
|
|
|
|
|
|
var receipt = Time.Wait(handler.SendRequestAndWaitForReceiptAsync(contractAddress, function));
|
|
|
|
|
|
if (!receipt.Succeeded()) throw new Exception("Unable to perform contract transaction.");
|
2023-12-15 11:02:06 +01:00
|
|
|
|
return receipt.TransactionHash;
|
2023-04-24 14:09:23 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-20 13:21:53 +01:00
|
|
|
|
public Transaction GetTransaction(string transactionHash)
|
|
|
|
|
|
{
|
|
|
|
|
|
log.Debug();
|
|
|
|
|
|
return Time.Wait(web3.Eth.Transactions.GetTransactionByHash.SendRequestAsync(transactionHash));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-09-19 13:58:45 +02:00
|
|
|
|
public decimal? GetSyncedBlockNumber()
|
|
|
|
|
|
{
|
|
|
|
|
|
log.Debug();
|
|
|
|
|
|
var sync = Time.Wait(web3.Eth.Syncing.SendRequestAsync());
|
|
|
|
|
|
var number = Time.Wait(web3.Eth.Blocks.GetBlockNumber.SendRequestAsync());
|
|
|
|
|
|
var numberOfBlocks = number.ToDecimal();
|
|
|
|
|
|
if (sync.IsSyncing) return null;
|
|
|
|
|
|
return numberOfBlocks;
|
|
|
|
|
|
}
|
2023-09-19 13:39:24 +02:00
|
|
|
|
|
2023-09-19 13:58:45 +02:00
|
|
|
|
public bool IsContractAvailable(string abi, string contractAddress)
|
|
|
|
|
|
{
|
|
|
|
|
|
log.Debug();
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var contract = web3.Eth.GetContract(abi, contractAddress);
|
|
|
|
|
|
return contract != null;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-12-19 15:30:46 +01:00
|
|
|
|
|
2024-03-27 15:39:42 +01:00
|
|
|
|
public List<EventLog<TEvent>> GetEvents<TEvent>(string address, BlockInterval blockRange) where TEvent : IEventDTO, new()
|
2023-12-19 15:30:46 +01:00
|
|
|
|
{
|
2024-03-27 15:39:42 +01:00
|
|
|
|
return GetEvents<TEvent>(address, blockRange.From, blockRange.To);
|
2023-12-19 15:30:46 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-19 15:43:26 +01:00
|
|
|
|
public List<EventLog<TEvent>> GetEvents<TEvent>(string address, ulong fromBlockNumber, ulong toBlockNumber) where TEvent : IEventDTO, new()
|
2023-12-19 15:30:46 +01:00
|
|
|
|
{
|
2025-06-26 14:10:57 +02:00
|
|
|
|
var logs = new List<FilterLog>();
|
|
|
|
|
|
var p = web3.Processing.Logs.CreateProcessor(
|
|
|
|
|
|
action: logs.Add,
|
|
|
|
|
|
minimumBlockConfirmations: 1,
|
2025-06-26 18:15:48 +02:00
|
|
|
|
criteria: l => l.IsLogForEvent<TEvent>()
|
2025-06-26 14:10:57 +02:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
var from = new BlockParameter(fromBlockNumber);
|
|
|
|
|
|
var to = new BlockParameter(toBlockNumber);
|
|
|
|
|
|
var ct = new CancellationTokenSource().Token;
|
|
|
|
|
|
Time.Wait(p.ExecuteAsync(toBlockNumber: to.BlockNumber, cancellationToken: ct, startAtBlockNumberIfNotProcessed: from.BlockNumber));
|
|
|
|
|
|
|
|
|
|
|
|
return logs
|
|
|
|
|
|
.Where(l => l.IsLogForEvent<TEvent>())
|
|
|
|
|
|
.Select(l => l.DecodeEvent<TEvent>())
|
|
|
|
|
|
.ToList();
|
2023-12-19 15:30:46 +01:00
|
|
|
|
}
|
2024-03-27 15:39:42 +01:00
|
|
|
|
|
|
|
|
|
|
public BlockInterval ConvertTimeRangeToBlockRange(TimeRange timeRange)
|
|
|
|
|
|
{
|
2025-07-10 13:24:49 +02:00
|
|
|
|
if (timeRange.To - timeRange.From < TimeSpan.FromSeconds(1.0))
|
|
|
|
|
|
throw new Exception(nameof(ConvertTimeRangeToBlockRange) + ": Time range too small.");
|
|
|
|
|
|
|
2024-03-27 15:39:42 +01:00
|
|
|
|
var wrapper = new Web3Wrapper(web3, log);
|
|
|
|
|
|
var blockTimeFinder = new BlockTimeFinder(blockCache, wrapper, log);
|
|
|
|
|
|
|
|
|
|
|
|
var fromBlock = blockTimeFinder.GetLowestBlockNumberAfter(timeRange.From);
|
|
|
|
|
|
var toBlock = blockTimeFinder.GetHighestBlockNumberBefore(timeRange.To);
|
|
|
|
|
|
|
|
|
|
|
|
if (fromBlock == null || toBlock == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new Exception("Failed to convert time range to block range.");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return new BlockInterval(
|
2024-05-31 10:16:57 +02:00
|
|
|
|
timeRange: timeRange,
|
2024-03-27 15:39:42 +01:00
|
|
|
|
from: fromBlock.Value,
|
|
|
|
|
|
to: toBlock.Value
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2024-04-13 09:19:20 +02:00
|
|
|
|
|
|
|
|
|
|
public BlockTimeEntry GetBlockForNumber(ulong number)
|
|
|
|
|
|
{
|
|
|
|
|
|
var wrapper = new Web3Wrapper(web3, log);
|
|
|
|
|
|
var blockTimeFinder = new BlockTimeFinder(blockCache, wrapper, log);
|
|
|
|
|
|
return blockTimeFinder.Get(number);
|
|
|
|
|
|
}
|
2025-05-04 11:20:33 +02:00
|
|
|
|
|
2025-05-06 20:33:37 +02:00
|
|
|
|
public BlockWithTransactions GetBlockWithTransactions(ulong number)
|
2025-05-04 11:20:33 +02:00
|
|
|
|
{
|
2025-05-22 13:55:03 +02:00
|
|
|
|
var retry = new Retry(nameof(GetBlockWithTransactions),
|
|
|
|
|
|
maxTimeout: TimeSpan.FromMinutes(1.0),
|
|
|
|
|
|
sleepAfterFail: TimeSpan.FromSeconds(1.0),
|
|
|
|
|
|
onFail: f => { },
|
|
|
|
|
|
failFast: false);
|
|
|
|
|
|
|
|
|
|
|
|
return retry.Run(() => Time.Wait(web3.Eth.Blocks.GetBlockWithTransactionsByNumber.SendRequestAsync(new BlockParameter(number))));
|
2025-05-04 11:20:33 +02:00
|
|
|
|
}
|
2023-04-18 15:33:12 +02:00
|
|
|
|
}
|
2023-04-14 10:51:35 +02:00
|
|
|
|
}
|