public WalletSendLayout() { this.Build(); SendInfo = new SendInfo(); _AssetsController = new AssetsController(this); _DeltasController = new DeltasController(this); vboxDataPaste.ModifyFg(StateType.Normal, Constants.Colors.DialogBackground.Gdk); eventboxSeperator.ModifyBg(StateType.Normal, Constants.Colors.Seperator.Gdk); eventboxSeperator1.ModifyBg(StateType.Normal, Constants.Colors.Seperator.Gdk); entryAmount.ModifyFg(StateType.Normal, Constants.Colors.Text2.Gdk); entryAmount.ModifyFont(Constants.Fonts.ActionBarSmall); labelBalance.ModifyFg(StateType.Normal, Constants.Colors.TextBlue.Gdk); labelBalance.ModifyFont(Constants.Fonts.LogBig); labelHeader.ModifyFg(StateType.Normal, Constants.Colors.TextHeader.Gdk); labelHeader.ModifyFont(Constants.Fonts.ActionBarBig); //error labels Apply((Label label) => { label.ModifyFg(StateType.Normal, Constants.Colors.Error.Gdk); label.ModifyFont(Constants.Fonts.ActionBarSmall); }, labelDestinationError, labelDataError, labelAmountError); //labels Apply((Label label) => { label.ModifyFg(StateType.Normal, Constants.Colors.LabelText.Gdk); label.ModifyFont(Constants.Fonts.ActionBarIntermediate); }, labelDestination, labelData, labelAmount, labelSelectAsset, labelBalanceHeader); //entries Apply((Entry entry) => { entry.ModifyBg(StateType.Normal, Constants.Colors.Seperator.Gdk); entry.ModifyText(StateType.Normal, Constants.Colors.Text.Gdk); entry.ModifyFont(Constants.Fonts.ActionBarSmall); entry.ModifyBase(StateType.Normal, Constants.Colors.ButtonUnselected.Gdk); }, entryDestination, entryAmount); txtData.ModifyFg(StateType.Normal, Constants.Colors.Text2.Gdk); txtData.ModifyFont(Constants.Fonts.ActionBarSmall); txtData.ModifyBase(StateType.Normal, Constants.Colors.ButtonUnselected.Gdk); txtData.ModifyText(StateType.Normal, Constants.Colors.Text.Gdk); _Tx = null; eventboxPasteAddress.ButtonPressEvent += delegate { try { var clipboard = Gtk.Clipboard.Get(Gdk.Atom.Intern("CLIPBOARD", true)); entryDestination.Text = clipboard.WaitForText(); } catch { } }; eventboxPasterData.ButtonPressEvent += delegate { try { var clipboard = Gtk.Clipboard.Get(Gdk.Atom.Intern("CLIPBOARD", true)); txtData.Buffer.Text = clipboard.WaitForText(); } catch { } }; vboxMainInner.Remove(vboxData); entryDestination.Changed += (sender, e) => { try { var value = ((Entry)sender).Text; var address = string.IsNullOrEmpty(value) ? null : new Address(value); SendInfo.Destination = address; labelDestinationError.Text = ""; vboxMainInner.Remove(vboxData); if (address != null && address.AddressType == AddressType.Contract) { vboxMainInner.Add(vboxData); vboxMainInner.ReorderChild(vboxData, 2); } } catch { SendInfo.Destination = null; labelDestinationError.Text = "Invalid address"; } UpdateButtons(); }; txtData.Buffer.Changed += (sender, e) => { var text = ((TextBuffer)sender).Text; if (string.IsNullOrWhiteSpace(text) || text.Trim().Length == 0) { SendInfo.Data = null; SendInfo.DataValid = true; labelDataError.Text = ""; } else { try { SendInfo.Data = JObject.Parse(text); //TODO: deeper validation SendInfo.DataValid = true; labelDataError.Text = ""; } catch { SendInfo.Data = null; SendInfo.DataValid = false; labelDataError.Text = "Invalid data"; } } UpdateButtons(); }; entryAmount.Changed += (sender, e) => { var text = ((Entry)sender).Text; if (text.Trim().Length == 0) { SendInfo.Amount = 0; labelAmountError.Text = ""; } else { Zen zen; if (!Zen.IsValidText(text, out zen)) { SendInfo.Amount = 0; labelAmountError.Text = "Invalid amount"; } else { SendInfo.Amount = zen.Kalapas; CheckAssetAmount(); } } UpdateButtons(); }; eventboxBack.ButtonPressEvent += Back; comboboxAsset.Model = _AssetsStore; var textRenderer = new CellRendererText(); comboboxAsset.PackStart(textRenderer, false); comboboxAsset.AddAttribute(textRenderer, "text", 1); comboboxAsset.Changed += (sender, e) => { var comboBox = sender as Gtk.ComboBox; TreeIter iter; if (comboBox.GetActiveIter(out iter)) { var value = new GLib.Value(); comboBox.Model.GetValue(iter, 0, ref value); byte[] asset = value.Val as byte[]; SendInfo.Asset = asset; } else { SendInfo.Asset = null; } // var assetMatadataList = App.Instance.AssetsMetadata.GetAssetMatadataList().Where(t => t.Asset.SequenceEqual(SendInfo.Asset)); // if (assetMatadataList.Count() != 0) // { // labelSelectedAsset.Text = assetMatadataList.First().Display; // labelSelectedAsset1.Text = assetMatadataList.First().Display; //} UpdateBalance(); }; TreeIter iterDefault; if (_AssetsStore.Find(t => t.SequenceEqual(Consensus.Tests.zhash), out iterDefault)) { comboboxAsset.SetActiveIter(iterDefault); } UpdateBalance(); eventboxSend.ButtonPressEvent += async delegate { SendInfo.Reset(); UpdateStatus(""); HideButtons(); if (SendInfo.Destination.AddressType == AddressType.Contract && SendInfo.Data != null) { try { var data = SendInfo.Data; byte[] firstData = null; if (data["first"] is JObject) { byte[] signature; var pubkey = Convert.FromBase64String(data["first"]["pubkey"].Value <string>()); var toSign = Convert.FromBase64String(data["first"]["toSign"].Value <string>()); //TODO: run on non-ui thread? if (!App.Instance.Wallet.SignData(pubkey, toSign, out signature)) { labelDataError.Text = "Could not sign data"; UpdateButtons(); return; } byte[] _data = Convert.FromBase64String(data["first"]["data"].Value <string>()); firstData = _data.Concat(signature).ToArray(); } else { firstData = Convert.FromBase64String(data["first"].ToString()); } SendInfo.Destination.Data = firstData; if (data["second"] is JObject) { SendInfo.Json = ContractUtilities.DataGenerator.parseJson(data.ToString()); SendInfo.NeedAutoTx = true; } } catch { labelDataError.Text = "Could not parse data"; UpdateButtons(); return; } } _Tx = await Task.Run(() => App.Instance.Wallet.Sign( SendInfo.Destination, SendInfo.Asset, SendInfo.Amount )); SendInfo.Signed = _Tx != null; if (!SendInfo.Signed) { Gtk.Application.Invoke(delegate { UpdateStatus("Could not sign transaction"); }); return; } await Task.Run(() => { try { WalletSendLayout.SendInfo.TxResult = App.Instance.Node.Transmit(WalletSendLayout.Tx).Result; if (WalletSendLayout.SendInfo.TxResult != BlockChain.BlockChain.TxResultEnum.Accepted) { Gtk.Application.Invoke(delegate { UpdateStatus($"Could not broadcast transaction ({WalletSendLayout.SendInfo.TxResult})"); }); } else if (!WalletSendLayout.SendInfo.NeedAutoTx) { Gtk.Application.Invoke(delegate { UpdateStatus("Transaction transmitted", true); }); } else { var outputIdx = WalletSendLayout.Tx.outputs.ToList().FindIndex(t => t.@lock is Consensus.Types.OutputLock.ContractLock); var outpoint = new Types.Outpoint(Merkle.transactionHasher.Invoke(WalletSendLayout.Tx), (uint)outputIdx); byte[] witnessData = ContractUtilities.DataGenerator.makeMessage( WalletSendLayout.SendInfo.Json, outpoint); var autoTxResult = new ExecuteContractAction() { ContractHash = WalletSendLayout.SendInfo.Destination.Bytes, Message = witnessData }.Publish().Result; WalletSendLayout.SendInfo.AutoTxCreated = autoTxResult.Item1; if (WalletSendLayout.SendInfo.AutoTxCreated) { WalletSendLayout.SendInfo.AutoTxResult = App.Instance.Node.Transmit(autoTxResult.Item2).Result; if (WalletSendLayout.SendInfo.AutoTxResult == BlockChain.BlockChain.TxResultEnum.Accepted) { Gtk.Application.Invoke(delegate { UpdateStatus("Transmitted", true); }); } else { Gtk.Application.Invoke(delegate { UpdateStatus($"Could not broadcast auto transaction ({WalletSendLayout.SendInfo.AutoTxResult})"); }); } } else { Gtk.Application.Invoke(delegate { UpdateStatus($"Could not execute contract"); }); } } } catch { Gtk.Application.Invoke(delegate { UpdateStatus("Error sending message to contract"); }); } }); }; }