/// <summary> /// Handle the data context changing. /// </summary> /// <param name="sender"> /// The event sender. /// </param> /// <param name="e"> /// The event arguments. /// </param> private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e) { TransactionUnlockViewModel oldViewModel = e.OldValue as TransactionUnlockViewModel; TransactionUnlockViewModel newViewModel = e.NewValue as TransactionUnlockViewModel; if (oldViewModel != null) { oldViewModel.ReadyToClose -= OnViewModelReadyToClose; } if (newViewModel != null) { newViewModel.ReadyToClose += OnViewModelReadyToClose; } }
/// <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())); } }
/// <summary> /// Handle the password being changed. /// </summary> /// <param name="sender"> /// The event sender. /// </param> /// <param name="e"> /// The event arguments. /// </param> private void OnPasswordChanged(object sender, RoutedEventArgs e) { TransactionUnlockViewModel vm = (TransactionUnlockViewModel)DataContext; vm.Password = ((PasswordBox)sender).SecurePassword; }