public EthereumTransactionViewModel(EthereumTransaction tx, EthereumConfig ethereumConfig) : base(tx, ethereumConfig, GetAmount(tx), GetFee(tx)) { From = tx.From; To = tx.To; GasPrice = EthereumConfig.WeiToGwei((decimal)tx.GasPrice); GasLimit = (decimal)tx.GasLimit; GasUsed = (decimal)tx.GasUsed; Fee = EthereumConfig.WeiToEth(tx.GasUsed * tx.GasPrice); IsInternal = tx.IsInternal; }
private static decimal GetFee(EthereumTransaction tx) { var result = 0m; if (tx.Type.HasFlag(BlockchainTransactionType.Output)) { result += EthereumConfig.WeiToEth(tx.GasUsed * tx.GasPrice); } tx.InternalTxs?.ForEach(t => result += GetFee(t)); return(result); }
public override async Task <Result <decimal> > GetBalanceAsync( string address, CancellationToken cancellationToken = default) { var requestUri = $"api?module=account&action=balance&address={address}&apikey={ApiKey}"; await RequestLimitControl .Wait(cancellationToken) .ConfigureAwait(false); return(await HttpHelper.GetAsyncResult <decimal>( baseUri : BaseUrl, requestUri : requestUri, responseHandler : (response, content) => { var json = JsonConvert.DeserializeObject <JObject>(content); return json.ContainsKey("result") ? EthereumConfig.WeiToEth(BigInteger.Parse(json["result"].ToString())) : 0; }, cancellationToken : cancellationToken) .ConfigureAwait(false)); }
public override Task UpdateBalanceAsync( string address, CancellationToken cancellationToken = default) { return(Task.Run(async() => { try { var eth = EthConfig; var walletAddress = await DataRepository .GetWalletAddressAsync(Currency, address) .ConfigureAwait(false); if (walletAddress == null) { return; } var balanceResult = await eth.BlockchainApi .TryGetBalanceAsync(address, cancellationToken: cancellationToken) .ConfigureAwait(false); if (balanceResult.HasError) { Log.Error("Error while balance update for {@address} with code {@code} and description {@description}", address, balanceResult.Error.Code, balanceResult.Error.Description); return; } var balance = balanceResult.Value; // calculate unconfirmed balances var unconfirmedTxs = (await DataRepository .GetUnconfirmedTransactionsAsync(Currency, eth.TransactionType) .ConfigureAwait(false)) .Cast <EthereumTransaction>() .ToList(); var unconfirmedInternalTxs = unconfirmedTxs.Aggregate(new List <EthereumTransaction>(), (list, tx) => { if (tx.InternalTxs != null) { list.AddRange(tx.InternalTxs); } return list; }); var unconfirmedIncome = 0m; var unconfirmedOutcome = 0m; foreach (var utx in unconfirmedTxs.Concat(unconfirmedInternalTxs)) { var isFailed = utx.State == BlockchainTransactionState.Failed; unconfirmedIncome += address == utx.To && !isFailed ? EthereumConfig.WeiToEth(utx.Amount) : 0; unconfirmedOutcome += address == utx.From && !isFailed ? -EthereumConfig.WeiToEth(utx.Amount + utx.GasPrice * (utx.GasUsed != 0 ? utx.GasUsed : utx.GasLimit)) : 0; } var balanceDifference = balance - walletAddress.Balance; var unconfirmedIncomeDifference = unconfirmedIncome - walletAddress.UnconfirmedIncome; var unconfirmedOutcomeDifference = unconfirmedOutcome - walletAddress.UnconfirmedOutcome; if (balanceDifference != 0 || unconfirmedIncomeDifference != 0 || unconfirmedOutcomeDifference != 0) { walletAddress.Balance = balance; walletAddress.UnconfirmedIncome = unconfirmedIncome; walletAddress.UnconfirmedOutcome = unconfirmedOutcome; walletAddress.HasActivity = true; await DataRepository.UpsertAddressAsync(walletAddress) .ConfigureAwait(false); Balance += balanceDifference; UnconfirmedIncome += unconfirmedIncomeDifference; UnconfirmedOutcome += unconfirmedOutcomeDifference; RaiseBalanceUpdated(new CurrencyEventArgs(Currency)); } } catch (OperationCanceledException) { Log.Debug("UpdateBalanceAsync canceled."); } catch (Exception e) { Log.Error(e, "UpdateBalanceAsync error."); } }, cancellationToken)); }
public override Task UpdateBalanceAsync( CancellationToken cancellationToken = default) { return(Task.Run(async() => { try { var eth = EthConfig; var txs = (await DataRepository .GetTransactionsAsync(Currency, eth.TransactionType) .ConfigureAwait(false)) .Cast <EthereumTransaction>() .ToList(); var internalTxs = txs.Aggregate(new List <EthereumTransaction>(), (list, tx) => { if (tx.InternalTxs != null) { list.AddRange(tx.InternalTxs); } return list; }); // calculate balances var totalUnconfirmedIncome = 0m; var totalUnconfirmedOutcome = 0m; var addressBalances = new Dictionary <string, WalletAddress>(); foreach (var tx in txs.Concat(internalTxs)) { var addresses = new HashSet <string>(); var isFromSelf = await IsSelfAddressAsync(tx.From, cancellationToken) .ConfigureAwait(false); if (isFromSelf) { addresses.Add(tx.From); } var isToSelf = await IsSelfAddressAsync(tx.To, cancellationToken) .ConfigureAwait(false); if (isToSelf) { addresses.Add(tx.To); } foreach (var address in addresses) { var isIncome = address == tx.To; var isOutcome = address == tx.From; var isConfirmed = tx.IsConfirmed; var isFailed = tx.State == BlockchainTransactionState.Failed; var income = isIncome && !isFailed ? EthereumConfig.WeiToEth(tx.Amount) : 0; var outcome = isOutcome ? (!isFailed ? -EthereumConfig.WeiToEth(tx.Amount + tx.GasPrice * (tx.GasUsed != 0 ? tx.GasUsed : tx.GasLimit)) : -EthereumConfig.WeiToEth(tx.GasPrice * tx.GasUsed)) : 0; if (addressBalances.TryGetValue(address, out var walletAddress)) { //walletAddress.Balance += isConfirmed ? income + outcome : 0; walletAddress.UnconfirmedIncome += !isConfirmed ? income : 0; walletAddress.UnconfirmedOutcome += !isConfirmed ? outcome : 0; } else { walletAddress = await DataRepository .GetWalletAddressAsync(Currency, address) .ConfigureAwait(false); //walletAddress.Balance = isConfirmed ? income + outcome : 0; walletAddress.UnconfirmedIncome = !isConfirmed ? income : 0; walletAddress.UnconfirmedOutcome = !isConfirmed ? outcome : 0; walletAddress.HasActivity = true; addressBalances.Add(address, walletAddress); } //totalBalance += isConfirmed ? income + outcome : 0; totalUnconfirmedIncome += !isConfirmed ? income : 0; totalUnconfirmedOutcome += !isConfirmed ? outcome : 0; } } var totalBalance = 0m; var api = eth.BlockchainApi; foreach (var wa in addressBalances.Values) { var balanceResult = await api .TryGetBalanceAsync( address: wa.Address, cancellationToken: cancellationToken) .ConfigureAwait(false); if (balanceResult.HasError) { Log.Error("Error while getting balance for {@address} with code {@code} and description {@description}", wa.Address, balanceResult.Error.Code, balanceResult.Error.Description); continue; // todo: may be return? } wa.Balance = balanceResult.Value; totalBalance += wa.Balance; } // upsert addresses await DataRepository .UpsertAddressesAsync(addressBalances.Values) .ConfigureAwait(false); Balance = totalBalance; UnconfirmedIncome = totalUnconfirmedIncome; UnconfirmedOutcome = totalUnconfirmedOutcome; RaiseBalanceUpdated(new CurrencyEventArgs(Currency)); } catch (OperationCanceledException) { Log.Debug($"{Currency} UpdateBalanceAsync canceled."); } catch (Exception e) { Log.Error(e, $"{Currency} UpdateBalanceAsync error."); } }, cancellationToken)); }