diff --git a/ethereal/assets/ext/filter.js b/ethereal/assets/ext/filter.js
new file mode 100644
index 0000000000000000000000000000000000000000..20f0b36a30856284fcc537dad3d3a20bb8e57a4f
--- /dev/null
+++ b/ethereal/assets/ext/filter.js
@@ -0,0 +1,23 @@
+var Filter = function(eth, options) {
+	this.callbacks = {};
+	this.seed = Math.floor(Math.random() * 1000000);
+	this.eth = eth;
+
+	eth.registerFilter(options, this.seed);
+};
+
+Filter.prototype.changed = function(callback) {
+	var cbseed = Math.floor(Math.random() * 1000000);
+	this.eth.registerFilterCallback(this.seed, cbseed);
+
+	var self = this;
+	message.connect(function(messages, seed, callbackSeed) {
+		if(seed ==  self.seed && callbackSeed == cbseed) {
+			callback.call(self, messages);
+		}
+	});
+};
+
+Filter.prototype.uninstall = function() {
+	eth.uninstallFilter(this.seed)
+}
diff --git a/ethereal/assets/qml/views/wallet.qml b/ethereal/assets/qml/views/wallet.qml
index 22b09640b4453ee308a39408de5d1a8676fa24ca..9afb1f89e9adcdd53801a2e55d28f53704932fcd 100644
--- a/ethereal/assets/qml/views/wallet.qml
+++ b/ethereal/assets/qml/views/wallet.qml
@@ -17,43 +17,145 @@ Rectangle {
 
 	function onReady() {
 		menuItem.secondary = eth.numberToHuman(eth.balanceAt(eth.key().address))
+	}
 
+	ListModel {
+		id: denomModel
+		ListElement { text: "Wei" ;     zeros: "" }
+		ListElement { text: "Ada" ;     zeros: "000" }
+		ListElement { text: "Babbage" ; zeros: "000000" }
+		ListElement { text: "Shannon" ; zeros: "000000000" }
+		ListElement { text: "Szabo" ;   zeros: "000000000000" }
+		ListElement { text: "Finney" ;  zeros: "000000000000000" }
+		ListElement { text: "Ether" ;   zeros: "000000000000000000" }
+		ListElement { text: "Einstein" ;zeros: "000000000000000000000" }
+		ListElement { text: "Douglas" ; zeros: "000000000000000000000000000000000000000000" }
 	}
 
 	ColumnLayout {
 		spacing: 10
 		y: 40
-		anchors {
-			left: parent.left
-			right: parent.right
-		}
+		anchors.fill: parent
 
 		Text {
+			id: balance
 			text: "<b>Balance</b>: " + eth.numberToHuman(eth.balanceAt(eth.key().address))
 			font.pixelSize: 24
 			anchors {
 				horizontalCenter: parent.horizontalCenter
+				top: parent.top
+				topMargin: 20
 			}
 		}
 
-		TableView {
-			id: txTableView
+		Rectangle {
+			id: newTxPane
+			color: "#ececec"
+			border.color: "#cccccc"
+			border.width: 1
 			anchors {
+				top: balance.bottom
+				topMargin: 10
 				left: parent.left
+				leftMargin: 5
 				right: parent.right
+				rightMargin: 5
+			}
+			height: 100
+
+			RowLayout {
+				id: amountFields
+				spacing: 10
+				anchors {
+					top: parent.top
+					topMargin: 20
+					left: parent.left
+					leftMargin: 20
+				}
+
+				Text {
+					text: "Ξ  "
+				}
+
+				// There's something off with the row layout where textfields won't listen to the width setting
+				Rectangle {
+					width: 50
+					height: 20
+					TextField {
+						id: txValue
+						width: parent.width
+						placeholderText: "0.00"
+					}
+				}
+
+				ComboBox {
+					id: valueDenom
+					currentIndex: 6
+					model: denomModel
+				}
+
 			}
-			TableViewColumn{ role: "num" ; title: "#" ; width: 30 }
-			TableViewColumn{ role: "from" ; title: "From" ; width: 280 }
-			TableViewColumn{ role: "to" ; title: "To" ; width: 280 }
-			TableViewColumn{ role: "value" ; title: "Amount" ; width: 100 }
-
-			model: ListModel {
-				id: txModel
-				Component.onCompleted: {
-					var messages = JSON.parse(eth.messages({latest: -1, from: "e6716f9544a56c530d868e4bfbacb172315bdead"}))
-					for(var i = 0; i < messages.length; i++) {
-						var message = messages[i];
-						this.insert(0, {num: i, from: message.from, to: message.to, value: eth.numberToHuman(message.value)})
+
+			RowLayout {
+				id: toFields
+				spacing: 10
+				anchors {
+					top: amountFields.bottom
+					topMargin: 5
+					left: parent.left
+					leftMargin: 20
+				}
+
+				Text {
+					text: "To"
+				}
+
+				Rectangle {
+					width: 200
+					height: 20
+					TextField {
+						id: txTo
+						width: parent.width
+						placeholderText: "Address or name"
+					}
+				}
+
+				Button {
+					text: "Send"
+					onClicked: {
+						var value = txValue.text + denomModel.get(valueDenom.currentIndex).zeros;
+						var gasPrice = "10000000000000"
+						var res = eth.transact(eth.key().privateKey, txTo.text, value, "500", gasPrice, "")
+						console.log(res)
+					}
+				}
+			}
+		}
+
+		Rectangle {
+			anchors {
+				left: parent.left
+				right: parent.right
+				top: newTxPane.bottom
+				topMargin: 10
+				bottom: parent.bottom
+			}
+			TableView {
+				id: txTableView
+				anchors.fill : parent
+				TableViewColumn{ role: "num" ; title: "#" ; width: 30 }
+				TableViewColumn{ role: "from" ; title: "From" ; width: 280 }
+				TableViewColumn{ role: "to" ; title: "To" ; width: 280 }
+				TableViewColumn{ role: "value" ; title: "Amount" ; width: 100 }
+
+				model: ListModel {
+					id: txModel
+					Component.onCompleted: {
+						var messages = JSON.parse(eth.messages({latest: -1, from: "e6716f9544a56c530d868e4bfbacb172315bdead"}))
+						for(var i = 0; i < messages.length; i++) {
+							var message = messages[i];
+							this.insert(0, {num: i, from: message.from, to: message.to, value: eth.numberToHuman(message.value)})
+						}
 					}
 				}
 			}
diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml
index 89126f600809d8d50cef83b3e79e392c9c21288f..122ee049e5db22bcbaf822f2d8c3ef1f8f697b24 100644
--- a/ethereal/assets/qml/wallet.qml
+++ b/ethereal/assets/qml/wallet.qml
@@ -6,17 +6,28 @@ import QtQuick.Window 2.1;
 import QtQuick.Controls.Styles 1.1
 import Ethereum 1.0
 
+import "../ext/filter.js" as Eth
 
 ApplicationWindow {
 	id: root
 
 	property alias miningButtonText: miningButton.text
 
+
 	width: 900
 	height: 600
 	minimumHeight: 300
 
-	title: "Ethereal"
+	title: "Ether browser"
+
+	// This signal is used by the filter API. The filter API connects using this signal handler from
+	// the different QML files and plugins.
+	signal message(var callback, int seed, int seedCallback);
+	function invokeFilterCallback(data, receiverSeed, callbackSeed) {
+		var messages = JSON.parse(data)
+		// Signal handler
+		message(data, receiverSeed, callbackSeed);
+	}
 
 	TextField {
 		id: copyElementHax
@@ -31,17 +42,17 @@ ApplicationWindow {
 
 	// Takes care of loading all default plugins
 	Component.onCompleted: {
-		var walletView = addPlugin("./views/wallet.qml", {section: "ethereum"})
-
-		var historyView = addPlugin("./views/history.qml", {section: "legacy"})
-		var newTxView = addPlugin("./views/transaction.qml", {section: "legacy"})
-		var chainView = addPlugin("./views/chain.qml", {section: "legacy"})
-		var infoView = addPlugin("./views/info.qml", {section: "legacy"})
-		var pendingTxView = addPlugin("./views/pending_tx.qml", {section: "legacy"})
-		var pendingTxView = addPlugin("./views/javascript.qml", {section: "legacy"})
+		var walletView = addPlugin("./views/wallet.qml", {noAdd: true, section: "ethereum", active: true})
+		var historyView = addPlugin("./views/history.qml", {noAdd: true, section: "legacy"})
+		var newTxView = addPlugin("./views/transaction.qml", {noAdd: true, section: "legacy"})
+		var chainView = addPlugin("./views/chain.qml", {noAdd: true, section: "legacy"})
+		var infoView = addPlugin("./views/info.qml", {noAdd: true, section: "legacy"})
+		var pendingTxView = addPlugin("./views/pending_tx.qml", {noAdd: true, section: "legacy"})
+		var pendingTxView = addPlugin("./views/javascript.qml", {noAdd: true, section: "legacy"})
 
 		// Call the ready handler
 		gui.done()
+
 	}
 
 	function addPlugin(path, options) {
@@ -50,10 +61,20 @@ ApplicationWindow {
 			if(component.status == Component.Error) {
 				console.debug("Error:"+ component.errorString());
 			}
+
 			return
 		}
 
-		return mainSplit.addComponent(component, options)
+		var views = mainSplit.addComponent(component, options)
+		views.menuItem.path = path
+
+		mainSplit.views.push(views);
+
+		if(!options.noAdd) {
+			gui.addPlugin(path)
+		}
+
+		return views.view
 	}
 
 	MenuBar {
@@ -76,7 +97,7 @@ ApplicationWindow {
 				text: "Add plugin"
 				onTriggered: {
 					generalFileDialog.show(true, function(path) {
-						addPlugin(path, {canClose: true})
+						addPlugin(path, {canClose: true, section: "apps"})
 					})
 				}
 			}
@@ -268,454 +289,441 @@ ApplicationWindow {
 
 		function addComponent(component, options) {
 			var view = mainView.createView(component, options)
+			view.visible = false
+			view.anchors.fill = mainView
 
-			if(!view.hasOwnProperty("iconFile")) {
+			if( !view.hasOwnProperty("iconFile") ) {
 				console.log("Could not load plugin. Property 'iconFile' not found on view.");
 				return;
 			}
 
 			var menuItem = menu.createMenuItem(view.iconFile, view, options);
-			if(view.hasOwnProperty("menuItem")) {
+			if( view.hasOwnProperty("menuItem") ) {
 				view.menuItem = menuItem;
 			}
-			mainSplit.views.push({view: view, menuItem: menuItem});
 
-			if(view.hasOwnProperty("onReady")) {
+			if( view.hasOwnProperty("onReady") ) {
 				view.onReady.call(view)
 			}
 
-			return view
-		}
-
-		/*********************
-		 * Main menu.
-		 ********************/
-		Rectangle {
-			id: menu
-			Layout.minimumWidth: 180
-			Layout.maximumWidth: 180
-			anchors.top: parent.top
-			color: "#ececec"
-
-			Component {
-				id: menuItemTemplate
-				Rectangle {
-					id: menuItem
-					property var view;
-
-					property alias title: label.text
-					property alias icon: icon.source
-					property alias secondary: secondary.text
-
-					width: 180
-					height: 28
-					border.color: "#00000000"
-					border.width: 1
-					radius: 5
-					color: "#00000000"
-
-					anchors {
-						left: parent.left
-						leftMargin: 4
-					}
-
-					Image {
-						id: icon
-						height: 20
-						width: 20
-						anchors {
-							left: parent.left
-							verticalCenter: parent.verticalCenter
-							leftMargin: 3
-						}
-					}
-
-					Text {
-						id: label
-						anchors {
-							left: icon.right
-							verticalCenter: parent.verticalCenter
-							leftMargin: 3
-						}
-
-						//font.bold: true
-						color: "#0D0A01"
-						font.pixelSize: 12
-					}
-
-					Text {
-						id: secondary
-						anchors {
-							right: parent.right
-							rightMargin: 8
-							verticalCenter: parent.verticalCenter
-						}
-						color: "#AEADBE"
-						font.pixelSize: 12
-					}
-
-					MouseArea {
-						anchors.fill: parent
-						onClicked: {
-							mainSplit.setView(view, menuItem)
-						}
-					}
-				}
+			if( options.active ) {
+				setView(view, menuItem)
 			}
 
-			function createMenuItem(icon, view, options) {
-				if(options === undefined) {
-					options = {};
-				}
-
-				var section;
-				switch(options.section) {
-				case "ethereum":
-					section = menuDefault;
-					break;
-				case "legacy":
-					section = menuLegacy;
-					break;
-				default:
-					section = menuApps;
-					break;
-				}
-						
-				var comp = menuItemTemplate.createObject(section)
-
-				comp.view = view
-				comp.title = view.title
-				comp.icon = view.iconFile
-				/*
-				if(view.secondary !== undefined) {
-					comp.secondary = view.secondary
-				}
-				*/
-
-				return comp
-
-				/*
-				if(options.canClose) {
-					//comp.closeButton.visible = options.canClose
-				}
-				*/
-			}
 
-			ColumnLayout {
-				id: menuColumn
-				y: 10
-				width: parent.width
-				anchors.left: parent.left
-				anchors.right: parent.right
-				spacing: 3
-
-				Text {
-					text: "ETHEREUM"
-					font.bold: true
-					anchors {
-						left: parent.left
-						leftMargin: 5
-					}
-					color: "#888888"
-				}
-
-				ColumnLayout {
-					id: menuDefault
-					spacing: 3
-					anchors {
-						left: parent.left
-						right: parent.right
-					}
-				}
-
-				Text {
-					text: "LEGACY"
-					font.bold: true
-					anchors {
-						left: parent.left
-						leftMargin: 5
-					}
-					color: "#888888"
-				}
-
-				ColumnLayout {
-					id: menuLegacy
-					spacing: 3
-					anchors {
-						left: parent.left
-						right: parent.right
-					}
-				}
-
-				Text {
-					text: "APPS"
-					font.bold: true
-					anchors {
-						left: parent.left
-						leftMargin: 5
-					}
-					color: "#888888"
-				}
-
-				/*
-				Rectangle {
-					width: 180
-					height: 28
-					border.color: "#CCCCCC"
-					border.width: 1
-					radius: 5
-					color: "#FFFFFF"
-
-					anchors {
-						left: parent.left
-						leftMargin: 4
-					}
-
-					Image {
-						id: icon
-						anchors {
-							left: parent.left
-							verticalCenter: parent.verticalCenter
-						}
-						source: "../pick.png"
-					}
-
-					Text {
-						anchors {
-							left: icon.right
-							verticalCenter: parent.verticalCenter
-						}
-
-						text: "Wallet"
-						font.bold: true
-						color: "#0D0A01"
-					}
-
-					Text {
-						anchors {
-							right: parent.right
-							rightMargin: 8
-							verticalCenter: parent.verticalCenter
-						}
-						color: "#AEADBE"
-						text: "12e15 Ξ"
-						font.pixelSize: 12
-					}
-				}
-				*/
-			}
+			return {view: view, menuItem: menuItem}
 		}
 
 		/*********************
-		 * Main view
+		 * Main menu.
 		 ********************/
-		Rectangle {
-			id: mainView
-			color: "#00000000"
-
-			anchors.right: parent.right
-			anchors.left: menu.right
-			anchors.bottom: parent.bottom
-			anchors.top: parent.top
-
-			function createView(component) {
-				var view = component.createObject(mainView)
-
-				return view;
-			}
-		}
-
-
-	}
-
-
-	/******************
-	 * Dialogs
-	 *****************/
-	FileDialog {
-		id: generalFileDialog
-		property var callback;
-		onAccepted: {
-			var path = this.fileUrl.toString();
-			callback.call(this, path);
-		}
-
-		function show(selectExisting, callback) {
-			generalFileDialog.callback = callback;
-			generalFileDialog.selectExisting = selectExisting;
-
-			this.open();
-		}
-	}
-
-
-	/******************
-	 * Wallet functions
-	 *****************/
-	function importApp(path) {
-		var ext = path.split('.').pop()
-		if(ext == "html" || ext == "htm") {
-			eth.openHtml(path)
-		}else if(ext == "qml"){
-			eth.openQml(path)
-		}
-	}
-
-	function setWalletValue(value) {
-		walletValueLabel.text = value
-	}
-
-	function loadPlugin(name) {
-		console.log("Loading plugin" + name)
-		mainView.addPlugin(name)
-	}
-
-	function setPeers(text) {
-		peerLabel.text = text
-	}
-
-	function addPeer(peer) {
-		// We could just append the whole peer object but it cries if you try to alter them
-		peerModel.append({ip: peer.ip, port: peer.port, lastResponse:timeAgo(peer.lastSend), latency: peer.latency, version: peer.version})
-	}
-
-	function resetPeers(){
-		peerModel.clear()
-	}
-
-	function timeAgo(unixTs){
-		var lapsed = (Date.now() - new Date(unixTs*1000)) / 1000
-		return  (lapsed + " seconds ago")
-	}
-
-	function convertToPretty(unixTs){
-		var a = new Date(unixTs*1000);
-		var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
-		var year = a.getFullYear();
-		var month = months[a.getMonth()];
-		var date = a.getDate();
-		var hour = a.getHours();
-		var min = a.getMinutes();
-		var sec = a.getSeconds();
-		var time = date+' '+month+' '+year+' '+hour+':'+min+':'+sec ;
-		return time;
-	}
-
-	/**********************
-	 * Windows
-	 *********************/
-	Window {
-		id: peerWindow
-		//flags: Qt.CustomizeWindowHint | Qt.Tool | Qt.WindowCloseButtonHint
-		height: 200
-		width: 700
-		Rectangle {
-			anchors.fill: parent
-			property var peerModel: ListModel {
-				id: peerModel
-			}
-			TableView {
-				anchors.fill: parent
-				id: peerTable
-				model: peerModel
-				TableViewColumn{width: 100; role: "ip" ; title: "IP" }
-				TableViewColumn{width: 60; role: "port" ; title: "Port" }
-				TableViewColumn{width: 140; role: "lastResponse"; title: "Last event" }
-				TableViewColumn{width: 100; role: "latency"; title: "Latency" }
-				TableViewColumn{width: 260; role: "version" ; title: "Version" }
-			}
-		}
-	}
-
-	Window {
-		id: aboutWin
-		visible: false
-		title: "About"
-		minimumWidth: 350
-		maximumWidth: 350
-		maximumHeight: 200
-		minimumHeight: 200
-
-		Image {
-			id: aboutIcon
-			height: 150
-			width: 150
-			fillMode: Image.PreserveAspectFit
-			smooth: true
-			source: "../facet.png"
-			x: 10
-			y: 10
-		}
-
-		Text {
-			anchors.left: aboutIcon.right
-			anchors.leftMargin: 10
-			font.pointSize: 12
-			text: "<h2>Ethereal - Adrastea</h2><br><h3>Development</h3>Jeffrey Wilcke<br>Maran Hidskes<br>Viktor Trón<br>"
-		}
-	}
-
-	Window {
-		id: txImportDialog
-		minimumWidth: 270
-		maximumWidth: 270
-		maximumHeight: 50
-		minimumHeight: 50
-		TextField {
-			id: txImportField
-			width: 170
-			anchors.verticalCenter: parent.verticalCenter
-			anchors.left: parent.left
-			anchors.leftMargin: 10
-			onAccepted: {
-			}
-		}
-		Button {
-			anchors.left: txImportField.right
-			anchors.verticalCenter: parent.verticalCenter
-			anchors.leftMargin: 5
-			text: "Import"
-			onClicked: {
-				eth.importTx(txImportField.text)
-				txImportField.visible = false
-			}
-		}
-		Component.onCompleted: {
-			addrField.focus = true
-		}
-	}
-
-	Window {
-		id: addPeerWin
-		visible: false
-		minimumWidth: 230
-		maximumWidth: 230
-		maximumHeight: 50
-		minimumHeight: 50
-
-		TextField {
-			id: addrField
-			anchors.verticalCenter: parent.verticalCenter
-			anchors.left: parent.left
-			anchors.leftMargin: 10
-			placeholderText: "address:port"
-			onAccepted: {
-				eth.connectToPeer(addrField.text)
-				addPeerWin.visible = false
-			}
-		}
-		Button {
-			anchors.left: addrField.right
-			anchors.verticalCenter: parent.verticalCenter
-			anchors.leftMargin: 5
-			text: "Add"
-			onClicked: {
-				eth.connectToPeer(addrField.text)
-				addPeerWin.visible = false
-			}
-		}
-		Component.onCompleted: {
-			addrField.focus = true
-		}
-	}
-}
+		 Rectangle {
+			 id: menu
+			 Layout.minimumWidth: 180
+			 Layout.maximumWidth: 180
+			 anchors.top: parent.top
+			 color: "#ececec"
+
+			 Component {
+				 id: menuItemTemplate
+				 Rectangle {
+					 id: menuItem
+					 property var view;
+					 property var path;
+
+					 property alias title: label.text
+					 property alias icon: icon.source
+					 property alias secondary: secondary.text
+
+					 width: 180
+					 height: 28
+					 border.color: "#00000000"
+					 border.width: 1
+					 radius: 5
+					 color: "#00000000"
+
+					 anchors {
+						 left: parent.left
+						 leftMargin: 4
+					 }
+
+					 MouseArea {
+						 anchors.fill: parent
+						 onClicked: {
+							 mainSplit.setView(view, menuItem)
+						 }
+					 }
+
+					 Image {
+						 id: icon
+						 height: 20
+						 width: 20
+						 anchors {
+							 left: parent.left
+							 verticalCenter: parent.verticalCenter
+							 leftMargin: 3
+						 }
+						 MouseArea {
+							 anchors.fill: parent
+							 onClicked: {
+								 menuItem.closeApp()
+							 }
+						 }
+					 }
+
+					 Text {
+						 id: label
+						 anchors {
+							 left: icon.right
+							 verticalCenter: parent.verticalCenter
+							 leftMargin: 3
+						 }
+
+						 color: "#0D0A01"
+						 font.pixelSize: 12
+					 }
+
+					 Text {
+						 id: secondary
+						 anchors {
+							 right: parent.right
+							 rightMargin: 8
+							 verticalCenter: parent.verticalCenter
+						 }
+						 color: "#AEADBE"
+						 font.pixelSize: 12
+					 }
+
+
+					 function closeApp() {
+						 if(this.view.hasOwnProperty("onDestroy")) {
+							 this.view.onDestroy.call(this.view)
+						 }
+
+						 this.view.destroy()
+						 this.destroy()
+						 gui.removePlugin(this.path)
+					 }
+				 }
+			 }
+
+			 function createMenuItem(icon, view, options) {
+				 if(options === undefined) {
+					 options = {};
+				 }
+
+				 var section;
+				 switch(options.section) {
+					 case "ethereum":
+					 section = menuDefault;
+					 break;
+					 case "legacy":
+					 section = menuLegacy;
+					 break;
+					 default:
+					 section = menuApps;
+					 break;
+				 }
+
+				 var comp = menuItemTemplate.createObject(section)
+
+				 comp.view = view
+				 comp.title = view.title
+				 comp.icon = view.iconFile
+				 /*
+				  if(view.secondary !== undefined) {
+					  comp.secondary = view.secondary
+				  }
+				  */
+
+				 return comp
+
+				 /*
+				  if(options.canClose) {
+					  //comp.closeButton.visible = options.canClose
+				  }
+				  */
+			 }
+
+			 ColumnLayout {
+				 id: menuColumn
+				 y: 10
+				 width: parent.width
+				 anchors.left: parent.left
+				 anchors.right: parent.right
+				 spacing: 3
+
+				 Text {
+					 text: "ETHEREUM"
+					 font.bold: true
+					 anchors {
+						 left: parent.left
+						 leftMargin: 5
+					 }
+					 color: "#888888"
+				 }
+
+				 ColumnLayout {
+					 id: menuDefault
+					 spacing: 3
+					 anchors {
+						 left: parent.left
+						 right: parent.right
+					 }
+				 }
+
+
+				 Text {
+					 text: "APPS"
+					 font.bold: true
+					 anchors {
+						 left: parent.left
+						 leftMargin: 5
+					 }
+					 color: "#888888"
+				 }
+
+				 ColumnLayout {
+					 id: menuApps
+					 spacing: 3
+					 anchors {
+						 left: parent.left
+						 right: parent.right
+					 }
+				 }
+
+				 Text {
+					 text: "DEBUG"
+					 font.bold: true
+					 anchors {
+						 left: parent.left
+						 leftMargin: 5
+					 }
+					 color: "#888888"
+				 }
+
+				 ColumnLayout {
+					 id: menuLegacy
+					 spacing: 3
+					 anchors {
+						 left: parent.left
+						 right: parent.right
+					 }
+				 }
+			 }
+		 }
+
+		 /*********************
+		  * Main view
+		  ********************/
+		  Rectangle {
+			  id: mainView
+			  color: "#00000000"
+
+			  anchors.right: parent.right
+			  anchors.left: menu.right
+			  anchors.bottom: parent.bottom
+			  anchors.top: parent.top
+
+			  function createView(component) {
+				  var view = component.createObject(mainView)
+
+				  return view;
+			  }
+		  }
+
+
+	  }
+
+
+	  /******************
+	   * Dialogs
+	   *****************/
+	   FileDialog {
+		   id: generalFileDialog
+		   property var callback;
+		   onAccepted: {
+			   var path = this.fileUrl.toString();
+			   callback.call(this, path);
+		   }
+
+		   function show(selectExisting, callback) {
+			   generalFileDialog.callback = callback;
+			   generalFileDialog.selectExisting = selectExisting;
+
+			   this.open();
+		   }
+	   }
+
+
+	   /******************
+	    * Wallet functions
+	    *****************/
+	    function importApp(path) {
+		    var ext = path.split('.').pop()
+		    if(ext == "html" || ext == "htm") {
+			    eth.openHtml(path)
+		    }else if(ext == "qml"){
+			    addPlugin(path, {canClose: true, section: "apps"})
+		    }
+	    }
+
+
+	    function setWalletValue(value) {
+		    walletValueLabel.text = value
+	    }
+
+	    function loadPlugin(name) {
+		    console.log("Loading plugin" + name)
+		    var view = mainView.addPlugin(name)
+	    }
+
+	    function setPeers(text) {
+		    peerLabel.text = text
+	    }
+
+	    function addPeer(peer) {
+		    // We could just append the whole peer object but it cries if you try to alter them
+		    peerModel.append({ip: peer.ip, port: peer.port, lastResponse:timeAgo(peer.lastSend), latency: peer.latency, version: peer.version})
+	    }
+
+	    function resetPeers(){
+		    peerModel.clear()
+	    }
+
+	    function timeAgo(unixTs){
+		    var lapsed = (Date.now() - new Date(unixTs*1000)) / 1000
+		    return  (lapsed + " seconds ago")
+	    }
+
+	    function convertToPretty(unixTs){
+		    var a = new Date(unixTs*1000);
+		    var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
+		    var year = a.getFullYear();
+		    var month = months[a.getMonth()];
+		    var date = a.getDate();
+		    var hour = a.getHours();
+		    var min = a.getMinutes();
+		    var sec = a.getSeconds();
+		    var time = date+' '+month+' '+year+' '+hour+':'+min+':'+sec ;
+		    return time;
+	    }
+
+	    /**********************
+	     * Windows
+	     *********************/
+	     Window {
+		     id: peerWindow
+		     //flags: Qt.CustomizeWindowHint | Qt.Tool | Qt.WindowCloseButtonHint
+		     height: 200
+		     width: 700
+		     Rectangle {
+			     anchors.fill: parent
+			     property var peerModel: ListModel {
+				     id: peerModel
+			     }
+			     TableView {
+				     anchors.fill: parent
+				     id: peerTable
+				     model: peerModel
+				     TableViewColumn{width: 100; role: "ip" ; title: "IP" }
+				     TableViewColumn{width: 60; role: "port" ; title: "Port" }
+				     TableViewColumn{width: 140; role: "lastResponse"; title: "Last event" }
+				     TableViewColumn{width: 100; role: "latency"; title: "Latency" }
+				     TableViewColumn{width: 260; role: "version" ; title: "Version" }
+			     }
+		     }
+	     }
+
+	     Window {
+		     id: aboutWin
+		     visible: false
+		     title: "About"
+		     minimumWidth: 350
+		     maximumWidth: 350
+		     maximumHeight: 200
+		     minimumHeight: 200
+
+		     Image {
+			     id: aboutIcon
+			     height: 150
+			     width: 150
+			     fillMode: Image.PreserveAspectFit
+			     smooth: true
+			     source: "../facet.png"
+			     x: 10
+			     y: 10
+		     }
+
+		     Text {
+			     anchors.left: aboutIcon.right
+			     anchors.leftMargin: 10
+			     font.pointSize: 12
+			     text: "<h2>Ethereal - Adrastea</h2><br><h3>Development</h3>Jeffrey Wilcke<br>Maran Hidskes<br>Viktor Trón<br>"
+		     }
+	     }
+
+	     Window {
+		     id: txImportDialog
+		     minimumWidth: 270
+		     maximumWidth: 270
+		     maximumHeight: 50
+		     minimumHeight: 50
+		     TextField {
+			     id: txImportField
+			     width: 170
+			     anchors.verticalCenter: parent.verticalCenter
+			     anchors.left: parent.left
+			     anchors.leftMargin: 10
+			     onAccepted: {
+			     }
+		     }
+		     Button {
+			     anchors.left: txImportField.right
+			     anchors.verticalCenter: parent.verticalCenter
+			     anchors.leftMargin: 5
+			     text: "Import"
+			     onClicked: {
+				     eth.importTx(txImportField.text)
+				     txImportField.visible = false
+			     }
+		     }
+		     Component.onCompleted: {
+			     addrField.focus = true
+		     }
+	     }
+
+	     Window {
+		     id: addPeerWin
+		     visible: false
+		     minimumWidth: 230
+		     maximumWidth: 230
+		     maximumHeight: 50
+		     minimumHeight: 50
+
+		     TextField {
+			     id: addrField
+			     anchors.verticalCenter: parent.verticalCenter
+			     anchors.left: parent.left
+			     anchors.leftMargin: 10
+			     placeholderText: "address:port"
+			     onAccepted: {
+				     eth.connectToPeer(addrField.text)
+				     addPeerWin.visible = false
+			     }
+		     }
+		     Button {
+			     anchors.left: addrField.right
+			     anchors.verticalCenter: parent.verticalCenter
+			     anchors.leftMargin: 5
+			     text: "Add"
+			     onClicked: {
+				     eth.connectToPeer(addrField.text)
+				     addPeerWin.visible = false
+			     }
+		     }
+		     Component.onCompleted: {
+			     addrField.focus = true
+		     }
+	     }
+     }
diff --git a/ethereal/gui.go b/ethereal/gui.go
index 1765c3fb26ec9b915e8322738390bc290e4b7dd7..3f989fe5115e9bbd44d3064ba06a35c1cb6f4c01 100644
--- a/ethereal/gui.go
+++ b/ethereal/gui.go
@@ -2,6 +2,7 @@ package main
 
 import (
 	"bytes"
+	"encoding/json"
 	"fmt"
 	"math/big"
 	"os"
@@ -24,6 +25,11 @@ import (
 
 var logger = ethlog.NewLogger("GUI")
 
+type plugin struct {
+	Name string `json:"name"`
+	Path string `json:"path"`
+}
+
 type Gui struct {
 	// The main application window
 	win *qml.Window
@@ -48,6 +54,8 @@ type Gui struct {
 	clientIdentity *ethwire.SimpleClientIdentity
 	config         *ethutil.ConfigManager
 
+	plugins map[string]plugin
+
 	miner *ethminer.Miner
 }
 
@@ -59,8 +67,16 @@ func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, clientIden
 	}
 
 	pipe := ethpipe.NewJSPipe(ethereum)
+	gui := &Gui{eth: ethereum, txDb: db, pipe: pipe, logLevel: ethlog.LogLevel(logLevel), Session: session, open: false, clientIdentity: clientIdentity, config: config, plugins: make(map[string]plugin)}
+	data, err := ethutil.ReadAllFile(ethutil.Config.ExecPath + "/plugins.json")
+	if err != nil {
+		fmt.Println(err)
+	}
+	fmt.Println(string(data))
 
-	return &Gui{eth: ethereum, txDb: db, pipe: pipe, logLevel: ethlog.LogLevel(logLevel), Session: session, open: false, clientIdentity: clientIdentity, config: config}
+	json.Unmarshal([]byte(data), &gui.plugins)
+
+	return gui
 }
 
 func (gui *Gui) Start(assetPath string) {
@@ -193,6 +209,7 @@ func (self *Gui) DumpState(hash, path string) {
 // The done handler will be called by QML when all views have been loaded
 func (gui *Gui) Done() {
 	gui.qmlDone = true
+
 }
 
 func (gui *Gui) ImportKey(filePath string) {
@@ -375,6 +392,10 @@ func (gui *Gui) update() {
 		gui.readPreviousTransactions()
 	}()
 
+	for _, plugin := range gui.plugins {
+		gui.win.Root().Call("addPlugin", plugin.Path, "")
+	}
+
 	var (
 		blockChan     = make(chan ethreact.Event, 100)
 		txChan        = make(chan ethreact.Event, 100)
@@ -504,7 +525,16 @@ func (gui *Gui) address() []byte {
 }
 
 func (gui *Gui) Transact(recipient, value, gas, gasPrice, d string) (*ethpipe.JSReceipt, error) {
-	data := ethutil.Bytes2Hex(utils.FormatTransactionData(d))
+	var data string
+	if len(recipient) == 0 {
+		code, err := ethutil.Compile(d, false)
+		if err != nil {
+			return nil, err
+		}
+		data = ethutil.Bytes2Hex(code)
+	} else {
+		data = ethutil.Bytes2Hex(utils.FormatTransactionData(d))
+	}
 
 	return gui.pipe.Transact(gui.privateKey(), recipient, value, gas, gasPrice, data)
 }
@@ -528,6 +558,20 @@ func (gui *Gui) GetLogLevel() ethlog.LogLevel {
 	return gui.logLevel
 }
 
+func (self *Gui) AddPlugin(pluginPath string) {
+	self.plugins[pluginPath] = plugin{Name: "SomeName", Path: pluginPath}
+
+	json, _ := json.MarshalIndent(self.plugins, "", "    ")
+	ethutil.WriteFile(ethutil.Config.ExecPath+"/plugins.json", json)
+}
+
+func (self *Gui) RemovePlugin(pluginPath string) {
+	delete(self.plugins, pluginPath)
+
+	json, _ := json.MarshalIndent(self.plugins, "", "    ")
+	ethutil.WriteFile(ethutil.Config.ExecPath+"/plugins.json", json)
+}
+
 // this extra function needed to give int typecast value to gui widget
 // that sets initial loglevel to default
 func (gui *Gui) GetLogLevelInt() int {
diff --git a/ethereal/ui_lib.go b/ethereal/ui_lib.go
index ade9bf381597a3fa6b1d6c54ec6d5e43082e2da6..7b2627c49c018c45b5b83dcc868219d44a2a126a 100644
--- a/ethereal/ui_lib.go
+++ b/ethereal/ui_lib.go
@@ -11,6 +11,7 @@ import (
 	"github.com/ethereum/eth-go/ethchain"
 	"github.com/ethereum/eth-go/ethcrypto"
 	"github.com/ethereum/eth-go/ethpipe"
+	"github.com/ethereum/eth-go/ethstate"
 	"github.com/ethereum/eth-go/ethutil"
 	"github.com/ethereum/go-ethereum/javascript"
 	"gopkg.in/qml.v1"
@@ -34,10 +35,13 @@ type UiLib struct {
 	DbWindow *DebuggerWindow
 
 	jsEngine *javascript.JSRE
+
+	filterCallbacks map[int][]int
+	filters         map[int]*GuiFilter
 }
 
 func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib {
-	return &UiLib{JSPipe: ethpipe.NewJSPipe(eth), engine: engine, eth: eth, assetPath: assetPath, jsEngine: javascript.NewJSRE(eth)}
+	return &UiLib{JSPipe: ethpipe.NewJSPipe(eth), engine: engine, eth: eth, assetPath: assetPath, jsEngine: javascript.NewJSRE(eth), filterCallbacks: make(map[int][]int), filters: make(map[int]*GuiFilter)}
 }
 
 func (self *UiLib) LookupDomain(domain string) string {
@@ -155,3 +159,31 @@ func (self *UiLib) StartDebugger() {
 
 	dbWindow.Show()
 }
+
+func (self *UiLib) RegisterFilter(object map[string]interface{}, seed int) {
+	filter := &GuiFilter{ethpipe.NewJSFilterFromMap(object, self.eth), seed}
+	self.filters[seed] = filter
+
+	filter.MessageCallback = func(messages ethstate.Messages) {
+		for _, callbackSeed := range self.filterCallbacks[seed] {
+			self.win.Root().Call("invokeFilterCallback", filter.MessagesToJson(messages), seed, callbackSeed)
+		}
+	}
+}
+
+func (self *UiLib) RegisterFilterCallback(seed, cbSeed int) {
+	self.filterCallbacks[seed] = append(self.filterCallbacks[seed], cbSeed)
+}
+
+func (self *UiLib) UninstallFilter(seed int) {
+	filter := self.filters[seed]
+	if filter != nil {
+		filter.Uninstall()
+		delete(self.filters, seed)
+	}
+}
+
+type GuiFilter struct {
+	*ethpipe.JSFilter
+	seed int
+}