Example #1
0
 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;
 }
Example #2
0
        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);
        }
Example #3
0
        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));
        }
Example #4
0
        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));
        }
Example #5
0
        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));
        }