Example #1
0
        /// <summary>
        /// Handle a fatal exception by calling our logger and then informing the user if possible.
        /// </summary>
        /// <param name="ex">
        /// The exception that triggered this.
        /// </param>
        private static void HandleFatalException(Exception ex)
        {
            try
            {
                Logger.ApplicationInstance.Error("Fatal error within Sift.", ex);
            }
            catch
            {
                // Intentionally swallowed
            }
            string msg = "There was a fatal error within SIFT, please contact support with this message." + Environment.NewLine + ex;

            try
            {
                SiftDialog.ShowDialog("Fatal Error", msg);
            }
            catch
            {
                try
                {
                    MessageBox.Show(msg);
                }
                catch
                {
                    // Intentionally swallowed
                }
            }
        }
Example #2
0
        /// <summary>
        /// Shows a SIFT dialog without any buttons in non-modal mode.
        /// </summary>
        /// <param name="title">
        /// The title of the message.
        /// </param>
        /// <param name="message">
        /// The message to show.
        /// </param>
        /// <param name="isEthereum">
        /// If this dialog is related to the ethereum network (true) and as such the ethereum logo should be displayed, otherwise false.
        /// </param>
        /// <returns>
        /// The dialog that was shown.
        /// </returns>
        public static SiftDialog ShowButtonless(string title, string message, bool isEthereum = false)
        {
            Func <SiftDialog> uiCallback = () =>
            {
                SiftDialogViewModel viewModel = new SiftDialogViewModel
                {
                    Title   = title,
                    Message = message,
                    IsEthereumAnimatedLogoVisible = isEthereum,
                    IsSiftLogoVisible             = !isEthereum,
                    IsLogButtonVisible            = true,
                    IsNoButtonVisible             = false,
                    IsReturnButtonVisible         = false,
                    IsYesButtonVisible            = false
                };
                SiftDialog dialog = new SiftDialog
                {
                    DataContext = viewModel,
                    Owner       = Application.Current.MainWindow
                };
                viewModel.CloseRequested += (s, e) => dialog.Close();
                dialog.Show();
                return(dialog);
            };

            if (Application.Current.Dispatcher.CheckAccess())
            {
                return(uiCallback());
            }
            else
            {
                return(Application.Current.Dispatcher.Invoke(uiCallback));
            }
        }
        /// <summary>
        /// Perform an investment in SIFT using the current UI settings.
        /// </summary>
        private async Task Invest()
        {
            // Disable UI
            SiftInvestIsEnabled = false;

            // Check times
            if (DateTime.UtcNow < _ethereumManager.IcoStartDate)
            {
                SiftDialog.ShowDialog("ICO Not Open", "The ICO for SIFT does not open until " + _ethereumManager.IcoStartDate.ToShortDateString() + " (00:00 GMT).  Please wait until this time to invest in SIFT.");
                return;
            }
            else if (DateTime.UtcNow > _ethereumManager.IcoEndDate)
            {
                SiftDialog.ShowDialog("ICO Not Open", "The ICO for SIFT finished at " + _ethereumManager.IcoStartDate.ToShortDateString() + " (00:00 GMT).  As soon as the ICO has been closed you will be able to purchase SIFT via an open marketplace.");
                return;
            }

            // Perform the purchase
            SiftPurchaseResponse response = await _ethereumManager.PurchaseSift(SelectedAccount.Address, SiftAmountToPurchase);

            if (!response.WasSuccessful)
            {
                SiftDialog.ShowDialog("SIFT Investment Problem", "Sorry, there was a problem processing your transaction.  Your SIFT could not be purchased at this time." + Environment.NewLine + Environment.NewLine + response.FailureReason);
            }
            else
            {
                TransactionToMine = _ethereumManager.EnqueueTransactionPendingReceipt(response.TransactionHash);
                if (TransactionToMine == null)
                {
                    SiftDialog.ShowDialog("SIFT Delayed Investment", "Your transaction to buy SIFT was successfully sent with hash " + response.TransactionHash + ", but we could not validate the transaction.  Your balance should update shortly, but if not please retry the transaction after checking your Ethereum wallet.");
                }
                else
                {
                    // Hookup to wait to hear the status, or process it immediately if we have it
                    Action act = () =>
                    {
                        TransactionToMine.PropertyChanged += OnTransactionToMinePropertyChanged;
                        _miningTransactionDialogViewModel  = new SiftDialogViewModel
                        {
                            IsEthereumAnimatedLogoVisible = true,
                            IsLogButtonVisible            = true,
                            Title   = "Confirming SIFT Investment",
                            Message = "Your transaction to invest in SIFT has been sent to the Ethereum network.  Depending on the current network congestion it may take anywhere between a few seconds and minutes for the transaction to confirm.  If the transaction does not confirm within a few minutes you can check your Ethereum wallet for more information." + Environment.NewLine + Environment.NewLine + "Please wait..."
                        };
                        _miningTransactionDialog = new SiftDialog
                        {
                            DataContext = _miningTransactionDialogViewModel,
                            Owner       = Application.Current.MainWindow
                        };
                        _miningTransactionDialogViewModel.CloseRequested += (s, e) =>
                        {
                            _miningTransactionDialog.Close();
                            _miningTransactionDialog          = null;
                            _miningTransactionDialogViewModel = null;
                        };
                        _miningTransactionDialog.ShowDialog();
                    };
                    if (Application.Current.Dispatcher.CheckAccess())
                    {
                        act();
                    }
                    else
                    {
                        Application.Current.Dispatcher.Invoke(act);
                    }
                }
            }

            // Update the UI
            UpdateSiftPurchaseSettings();
        }
        /// <summary>
        /// Purchase SIFT from one of our accounts.
        /// </summary>
        /// <param name="address">
        /// The account to use for purchase.
        /// </param>
        /// <param name="quantity">
        /// The quantity to purchase.
        /// </param>
        /// <returns>
        /// Details of the purchase result indicating whether a success or failure happened.
        /// </returns>
        public async Task <SiftPurchaseResponse> PurchaseSift(string address, uint quantity)
        {
            try
            {
                // Ensure we have the balance
                decimal         purchaseCostWei = WeiPerSift * quantity;
                EthereumAccount account         = Accounts.FirstOrDefault(acc => acc.Address == address);
                if (account == null)
                {
                    return(new SiftPurchaseResponse(SiftPurchaseFailureType.UnknownAccount, "The specified account address is not known."));
                }
                if (account.BalanceWei < purchaseCostWei)
                {
                    return(new SiftPurchaseResponse(SiftPurchaseFailureType.InsufficientFunds, "The specified account does not have sufficient funds."));
                }

                // Determine the gas cost for this
                TransactionGasInfo gasInfo = await CalculateGasCostForEtherSend(address, IcoContractAddress, purchaseCostWei);

                decimal gasPrice          = gasInfo.GasPrice;
                decimal gasCost           = gasInfo.GasCost;
                decimal gas               = gasInfo.Gas;
                decimal remainingGasMoney = account.BalanceWei - purchaseCostWei;
                if (remainingGasMoney < gasCost)
                {
                    return(new SiftPurchaseResponse(SiftPurchaseFailureType.InsufficientGas, "You do not have enough gas for this transaction."));
                }
                byte    gasMultiple = 25;
                decimal maximumGasCost;
                while ((maximumGasCost = gasCost * gasMultiple) * gasPrice > remainingGasMoney)
                {
                    gasMultiple--;
                }

                // Prompt to unlock account
                Logger.ApplicationInstance.Debug("Asking user confirmation for sending " + purchaseCostWei + " from " + address + " to " + IcoContractAddress);
                Func <TransactionUnlockViewModel> fnc = new Func <TransactionUnlockViewModel>(() =>
                {
                    TransactionUnlockViewModel vm  = new TransactionUnlockViewModel(gasCost, gasMultiple, gas, address, IcoContractAddress, purchaseCostWei);
                    TransactionUnlockWindow window = new TransactionUnlockWindow
                    {
                        DataContext = vm
                    };
                    window.ShowDialog();
                    return(vm);
                });
                TransactionUnlockViewModel viewModel = Application.Current.Dispatcher.Invoke(fnc);
                if (viewModel.WasCancelled)
                {
                    return(new SiftPurchaseResponse(SiftPurchaseFailureType.UserCancelled, "User cancelled transaction on confirmation screen."));
                }

                // Unlock the account
                SiftDialog dialog = SiftDialog.ShowButtonless("Sending Transaction", "Please wait whilst the transaction to buy SIFT is sent to the Ethereum network, this should only take a few seconds...", true);
                Logger.ApplicationInstance.Debug("Attempting to unlock " + address);
                if (!await _web3.Personal.UnlockAccount.SendRequestAsync(address, viewModel.Password == null ? string.Empty : viewModel.Password.ToString(), 120))
                {
                    return(new SiftPurchaseResponse(SiftPurchaseFailureType.UnlockError, "Unable to unlock account with the supplied password."));
                }

                // Send and mine the transaction
                Logger.ApplicationInstance.Debug("Account unlocked, sending " + purchaseCostWei + " from " + address + " to " + IcoContractAddress);
                TransactionInput transactionInput = new TransactionInput
                {
                    From     = address,
                    To       = IcoContractAddress,
                    Value    = new HexBigInteger(new BigInteger(purchaseCostWei)),
                    GasPrice = new HexBigInteger(new BigInteger(viewModel.SelectedGasMultiplier * gasCost)),
                    Gas      = new HexBigInteger(new BigInteger(viewModel.Gas))
                };
                Logger.ApplicationInstance.Info("Sending transaction for " + purchaseCostWei + " to " + IcoContractAddress + " from " + address);
                string transactionHash = await _web3.Eth.Transactions.SendTransaction.SendRequestAsync(transactionInput);

                Action act = dialog.Close;
                if (dialog.Dispatcher.CheckAccess())
                {
                    act();
                }
                else
                {
                    dialog.Dispatcher.Invoke(act);
                }

                // Return the response of the transaction hash
                return(new SiftPurchaseResponse(transactionHash));
            }
            catch (Exception ex)
            {
                Logger.ApplicationInstance.Error("Error purchasing SIFT", ex);
                if (ex.Message.Contains("personal_unlockAccount method not implemented"))
                {
                    return(new SiftPurchaseResponse(SiftPurchaseFailureType.MissingRpcPersonal, "Your geth installation doesn't have the personal RPC enabled, please enable it to continue."));
                }
                else if (ex.Message.Contains("could not decrypt key with given passphrase"))
                {
                    return(new SiftPurchaseResponse(SiftPurchaseFailureType.PasswordInvalid, "The password you supplied was incorrect"));
                }
                return(new SiftPurchaseResponse(SiftPurchaseFailureType.Unknown, ex.ToString()));
            }
        }
Example #5
0
        /// <summary>
        /// This method is the main entry point for the ethereum checks and it is responsible for changing message content.
        /// </summary>
        private void ThreadEntry()
        {
            // Always check for updates first
            StatusText   = "Checking for updates to sift-win...";
            StatusHeader = "Please Wait...";
            try
            {
                // Login to update API
                Logger.ApplicationInstance.Debug("Checking for updated version of SIFT");
                AuthenticationClient     authClient   = new AuthenticationClient();
                AuthenticateUserResponse authResponse = authClient.AuthenticateJwtAsync(new AuthenticateUserRequest
                {
                    Username = "******",
                    Password = "******"
                }).Result;
                if (authResponse == null)
                {
                    throw new Exception("Null response when checking authentication details");
                }

                // Call to get latest summary information
                ProductClient          client   = new ProductClient();
                ProductSummaryResponse response = client.ProductSummaryGetAsync("D7682386-897C-4798-84B3-911EDEB8BD44", "BEARER " + authResponse?.JsonWebToken).Result;
                if (response == null)
                {
                    throw new Exception("Null response when checking product details");
                }

                // Determine any changes to state an update UI accordingly
                Logger.ApplicationInstance.Debug("Latest version is " + response.LatestVersion + " from " + response.LatestDownloadUrl);
                if (!string.IsNullOrEmpty(response.LatestVersion))
                {
                    Logger.ApplicationInstance.Info("A new version of SIFT - version " + response.LatestVersion + " is available from " + response.LatestDownloadUrl);
                    Version version = Version.Parse(response.LatestVersion);
                    if (!System.Diagnostics.Debugger.IsAttached)
                    {
                        if (Assembly.GetEntryAssembly().GetName().Version < version && SiftDialog.ShowDialog("SIFT Upgrade Available", "A new version of SIFT is available (" + version + ").  Would you like to download it now?", true).Value)
                        {
                            System.Diagnostics.Process.Start(string.IsNullOrWhiteSpace(response.LatestDownloadUrl) ? "http://smartift.com/sift-win/latest" : response.LatestDownloadUrl);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.ApplicationInstance.Error("Failed to check for latest version", ex);
            }
            finally
            {
            }

            DateTime started = DateTime.UtcNow;

            StatusText = MessageConnecting;
            while (_isAlive)
            {
                // Break out of loop if we know what we're doing
                if (_ethereumManager.ContractPhase != ContractPhase.Unknown && !_ethereumManager.IsSyncing && _ethereumManager.LastChecksSuccessful && _ethereumManager.BlockNumber > 0 && _ethereumManager.Accounts.Count > 0)
                {
                    break;
                }

                // If we've just gone over 5 seconds display a "this may be broken" message and enable exit button
                if (DateTime.UtcNow > started.AddSeconds(5) || _ethereumManager.LastChecksSuccessful)
                {
                    StatusHeader = "Error!";
                    if (!_ethereumManager.LastChecksSuccessful || _ethereumManager.BlockNumber < 1)
                    {
                        StatusText = MessageDelayedStart;
                    }
                    else if (_ethereumManager.Accounts.Count < 1)
                    {
                        StatusText = MessageNoAddresses;
                    }
                    else if (_ethereumManager.IsSyncing)
                    {
                        StatusText = MessageSyncing;
                    }
                    else
                    {
                        StatusText = MessageUnknownContractState;
                    }
                    IsErrorState = true;
                }

                // Wait to retry
                Thread.Sleep(100);
            }

            // If we're still alive show appropriate window
            if (_isAlive)
            {
                // Show correct window and close the splash screen
                Action act = () =>
                {
                    Window mainWindow = Application.Current.MainWindow;
                    Window newWindow;
                    if (_ethereumManager.ContractPhase == ContractPhase.Ico)
                    {
                        newWindow = new IcoWindow {
                            DataContext = new IcoViewModel(_ethereumManager)
                        }
                    }
                    ;
                    else
                    {
                        newWindow = new PostIcoWindow {
                            DataContext = new PostIcoViewModel(_ethereumManager)
                        }
                    };
                    newWindow.Show();
                    Application.Current.MainWindow = newWindow;
                    mainWindow.Close();
                };
                Application.Current.Dispatcher.BeginInvoke(act);
            }
        }