forked from cerc-io/plugeth
Merge eth-go repository into go-ethereum
mist, etheruem have been moved to cmd/
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Controls 1.0;
|
||||
import QtQuick.Layouts 1.0;
|
||||
import Ethereum 1.0
|
||||
|
||||
ApplicationWindow {
|
||||
minimumWidth: 500
|
||||
maximumWidth: 500
|
||||
maximumHeight: 400
|
||||
minimumHeight: 400
|
||||
|
||||
function onNewBlockCb(block) {
|
||||
console.log("Please overwrite onNewBlock(block):", block)
|
||||
}
|
||||
function onObjectChangeCb(stateObject) {
|
||||
console.log("Please overwrite onObjectChangeCb(object)", stateObject)
|
||||
}
|
||||
function onStorageChangeCb(storageObject) {
|
||||
var ev = ["storage", storageObject.stateAddress, storageObject.address].join(":");
|
||||
console.log("Please overwrite onStorageChangeCb(object)", ev)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
import QtQuick 2.0
|
||||
import Ethereum 1.0
|
||||
|
||||
// Which ones do we actually need?
|
||||
import QtQuick.Controls 1.0;
|
||||
import QtQuick.Layouts 1.0;
|
||||
import QtQuick.Dialogs 1.0;
|
||||
import QtQuick.Window 2.1;
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
import QtQuick.Dialogs 1.1
|
||||
|
||||
ApplicationWindow {
|
||||
id: wizardRoot
|
||||
width: 500
|
||||
height: 400
|
||||
title: "Ethereal first run setup"
|
||||
|
||||
Column {
|
||||
spacing: 5
|
||||
anchors.leftMargin: 10
|
||||
anchors.left: parent.left
|
||||
|
||||
Text {
|
||||
visible: true
|
||||
text: "<h2>Ethereal setup</h2>"
|
||||
}
|
||||
|
||||
Column {
|
||||
id: restoreColumn
|
||||
spacing: 5
|
||||
Text {
|
||||
visible: true
|
||||
font.pointSize: 14
|
||||
text: "Restore your Ethereum account"
|
||||
id: restoreLabel
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: txPrivKey
|
||||
width: 480
|
||||
placeholderText: "Private key or mnemonic words"
|
||||
focus: true
|
||||
onTextChanged: {
|
||||
if(this.text.length == 64){
|
||||
detailLabel.text = "Private (hex) key detected."
|
||||
actionButton.enabled = true
|
||||
}
|
||||
else if(this.text.split(" ").length == 24){
|
||||
detailLabel.text = "Mnemonic key detected."
|
||||
actionButton.enabled = true
|
||||
}else{
|
||||
detailLabel.text = ""
|
||||
actionButton.enabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
Row {
|
||||
spacing: 10
|
||||
Button {
|
||||
id: actionButton
|
||||
text: "Restore"
|
||||
enabled: false
|
||||
onClicked: {
|
||||
var success = lib.importAndSetPrivKey(txPrivKey.text)
|
||||
if(success){
|
||||
importedDetails.visible = true
|
||||
restoreColumn.visible = false
|
||||
newKey.visible = false
|
||||
wizardRoot.height = 120
|
||||
}
|
||||
}
|
||||
}
|
||||
Text {
|
||||
id: detailLabel
|
||||
font.pointSize: 12
|
||||
anchors.topMargin: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
Column {
|
||||
id: importedDetails
|
||||
visible: false
|
||||
Text {
|
||||
text: "<b>Your account has been imported. Please close the application and restart it again to let the changes take effect.</b>"
|
||||
wrapMode: Text.WordWrap
|
||||
width: 460
|
||||
}
|
||||
}
|
||||
Column {
|
||||
spacing: 5
|
||||
id: newDetailsColumn
|
||||
visible: false
|
||||
Text {
|
||||
font.pointSize: 14
|
||||
text: "Your account details"
|
||||
}
|
||||
Label {
|
||||
text: "Address"
|
||||
}
|
||||
TextField {
|
||||
id: addressInput
|
||||
readOnly:true
|
||||
width: 480
|
||||
}
|
||||
Label {
|
||||
text: "Private key"
|
||||
}
|
||||
TextField {
|
||||
id: privkeyInput
|
||||
readOnly:true
|
||||
width: 480
|
||||
}
|
||||
Label {
|
||||
text: "Mnemonic words"
|
||||
}
|
||||
TextField {
|
||||
id: mnemonicInput
|
||||
readOnly:true
|
||||
width: 480
|
||||
}
|
||||
Label {
|
||||
text: "<b>A new account has been created. Please take the time to write down the <i>24 words</i>. You can use those to restore your account at a later date.</b>"
|
||||
wrapMode: Text.WordWrap
|
||||
width: 480
|
||||
}
|
||||
Label {
|
||||
text: "Please restart the application once you have completed the steps above."
|
||||
wrapMode: Text.WordWrap
|
||||
width: 480
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Button {
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.rightMargin: 10
|
||||
anchors.bottomMargin: 10
|
||||
id: newKey
|
||||
text: "I don't have an account yet"
|
||||
onClicked: {
|
||||
var res = lib.createAndSetPrivKey()
|
||||
mnemonicInput.text = res[0]
|
||||
addressInput.text = res[1]
|
||||
privkeyInput.text = res[2]
|
||||
|
||||
// Hide restore
|
||||
restoreColumn.visible = false
|
||||
|
||||
// Show new details
|
||||
newDetailsColumn.visible = true
|
||||
newKey.visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,908 @@
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Controls 1.0;
|
||||
import QtQuick.Layouts 1.0;
|
||||
import QtQuick.Dialogs 1.0;
|
||||
import QtQuick.Window 2.1;
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
import Ethereum 1.0
|
||||
|
||||
import "../ext/filter.js" as Eth
|
||||
import "../ext/http.js" as Http
|
||||
|
||||
ApplicationWindow {
|
||||
id: root
|
||||
|
||||
property alias miningButtonText: miningButton.text
|
||||
property var ethx : Eth.ethx
|
||||
property var browser
|
||||
|
||||
width: 1200
|
||||
height: 820
|
||||
minimumHeight: 300
|
||||
|
||||
title: "Mist"
|
||||
|
||||
// This signal is used by the filter API. The filter API connects using this signal handler from
|
||||
// the different QML files and plugins.
|
||||
signal messages(var messages, int id);
|
||||
function invokeFilterCallback(data, receiverSeed) {
|
||||
//var messages = JSON.parse(data)
|
||||
// Signal handler
|
||||
messages(data, receiverSeed);
|
||||
root.browser.view.messages(data, receiverSeed);
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: copyElementHax
|
||||
visible: false
|
||||
}
|
||||
|
||||
function copyToClipboard(text) {
|
||||
copyElementHax.text = text
|
||||
copyElementHax.selectAll()
|
||||
copyElementHax.copy()
|
||||
}
|
||||
|
||||
// Takes care of loading all default plugins
|
||||
Component.onCompleted: {
|
||||
var wallet = addPlugin("./views/wallet.qml", {noAdd: true, close: false, section: "ethereum", active: true});
|
||||
var browser = addPlugin("./webapp.qml", {noAdd: true, close: false, section: "ethereum", active: true});
|
||||
root.browser = browser;
|
||||
|
||||
addPlugin("./views/transaction.qml", {noAdd: true, close: false, section: "legacy"});
|
||||
addPlugin("./views/chain.qml", {noAdd: true, close: false, section: "legacy"});
|
||||
addPlugin("./views/pending_tx.qml", {noAdd: true, close: false, section: "legacy"});
|
||||
addPlugin("./views/info.qml", {noAdd: true, close: false, section: "legacy"});
|
||||
|
||||
addPlugin("./views/jeffcoin/jeffcoin.qml", {noAdd: true, close: false, section: "apps"})
|
||||
|
||||
mainSplit.setView(wallet.view, wallet.menuItem);
|
||||
|
||||
// Call the ready handler
|
||||
gui.done();
|
||||
}
|
||||
|
||||
function addViews(view, path, options) {
|
||||
var views = mainSplit.addComponent(view, options)
|
||||
views.menuItem.path = path
|
||||
|
||||
mainSplit.views.push(views);
|
||||
|
||||
if(!options.noAdd) {
|
||||
gui.addPlugin(path)
|
||||
}
|
||||
|
||||
return views
|
||||
}
|
||||
|
||||
function addPlugin(path, options) {
|
||||
try {
|
||||
if(typeof(path) === "string" && /^https?/.test(path)) {
|
||||
console.log('load http')
|
||||
Http.request(path, function(o) {
|
||||
if(o.status === 200) {
|
||||
var view = Qt.createQmlObject(o.responseText, mainView, path)
|
||||
addViews(view, path, options)
|
||||
}
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var component = Qt.createComponent(path);
|
||||
if(component.status != Component.Ready) {
|
||||
if(component.status == Component.Error) {
|
||||
ethx.note("error: ", component.errorString());
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var view = mainView.createView(component, options)
|
||||
var views = addViews(view, path, options)
|
||||
|
||||
return views
|
||||
} catch(e) {
|
||||
ethx.note(e)
|
||||
}
|
||||
}
|
||||
|
||||
menuBar: MenuBar {
|
||||
Menu {
|
||||
title: "File"
|
||||
MenuItem {
|
||||
text: "Import App"
|
||||
shortcut: "Ctrl+o"
|
||||
onTriggered: {
|
||||
generalFileDialog.show(true, importApp)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MenuItem {
|
||||
text: "Browser"
|
||||
onTriggered: eth.openBrowser()
|
||||
}
|
||||
*/
|
||||
|
||||
MenuItem {
|
||||
text: "Add plugin"
|
||||
onTriggered: {
|
||||
generalFileDialog.show(true, function(path) {
|
||||
addPlugin(path, {close: true, section: "apps"})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
MenuSeparator {}
|
||||
|
||||
MenuItem {
|
||||
text: "Import key"
|
||||
shortcut: "Ctrl+i"
|
||||
onTriggered: {
|
||||
generalFileDialog.show(true, function(path) {
|
||||
gui.importKey(path)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: "Export keys"
|
||||
shortcut: "Ctrl+e"
|
||||
onTriggered: {
|
||||
generalFileDialog.show(false, function(path) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Menu {
|
||||
title: "Developer"
|
||||
MenuItem {
|
||||
iconSource: "../icecream.png"
|
||||
text: "Debugger"
|
||||
shortcut: "Ctrl+d"
|
||||
onTriggered: eth.startDebugger()
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: "Import Tx"
|
||||
onTriggered: {
|
||||
txImportDialog.visible = true
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: "Run JS file"
|
||||
onTriggered: {
|
||||
generalFileDialog.show(true, function(path) {
|
||||
eth.evalJavascriptFile(path)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: "Dump state"
|
||||
onTriggered: {
|
||||
generalFileDialog.show(false, function(path) {
|
||||
// Empty hash for latest
|
||||
gui.dumpState("", path)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
MenuSeparator {}
|
||||
|
||||
/*
|
||||
MenuItem {
|
||||
id: miningSpeed
|
||||
text: "Mining: Turbo"
|
||||
onTriggered: {
|
||||
gui.toggleTurboMining()
|
||||
if(text == "Mining: Turbo") {
|
||||
text = "Mining: Normal";
|
||||
} else {
|
||||
text = "Mining: Turbo";
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
Menu {
|
||||
title: "Network"
|
||||
MenuItem {
|
||||
text: "Add Peer"
|
||||
shortcut: "Ctrl+p"
|
||||
onTriggered: {
|
||||
addPeerWin.visible = true
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
text: "Show Peers"
|
||||
shortcut: "Ctrl+e"
|
||||
onTriggered: {
|
||||
peerWindow.visible = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Menu {
|
||||
title: "Help"
|
||||
MenuItem {
|
||||
text: "About"
|
||||
onTriggered: {
|
||||
aboutWin.visible = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Menu {
|
||||
title: "GLOBAL SHORTCUTS"
|
||||
visible: false
|
||||
MenuItem {
|
||||
visible: false
|
||||
shortcut: "Ctrl+l"
|
||||
onTriggered: {
|
||||
url.focus = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
statusBar: StatusBar {
|
||||
height: 32
|
||||
id: statusBar
|
||||
RowLayout {
|
||||
Button {
|
||||
id: miningButton
|
||||
text: "Start Mining"
|
||||
onClicked: {
|
||||
gui.toggleMining()
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Label {
|
||||
id: walletValueLabel
|
||||
|
||||
font.pixelSize: 10
|
||||
styleColor: "#797979"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
y: 6
|
||||
objectName: "miningLabel"
|
||||
visible: true
|
||||
font.pixelSize: 10
|
||||
anchors.right: lastBlockLabel.left
|
||||
anchors.rightMargin: 5
|
||||
}
|
||||
|
||||
Label {
|
||||
y: 6
|
||||
id: lastBlockLabel
|
||||
objectName: "lastBlockLabel"
|
||||
visible: true
|
||||
text: ""
|
||||
font.pixelSize: 10
|
||||
anchors.right: peerGroup.left
|
||||
anchors.rightMargin: 5
|
||||
}
|
||||
|
||||
ProgressBar {
|
||||
id: downloadIndicator
|
||||
value: 0
|
||||
objectName: "downloadIndicator"
|
||||
y: 3
|
||||
x: statusBar.width / 2 - this.width / 2
|
||||
width: 160
|
||||
}
|
||||
|
||||
Label {
|
||||
objectName: "downloadLabel"
|
||||
y: 7
|
||||
anchors.left: downloadIndicator.right
|
||||
anchors.leftMargin: 5
|
||||
font.pixelSize: 10
|
||||
text: "0 / 0"
|
||||
}
|
||||
|
||||
|
||||
RowLayout {
|
||||
id: peerGroup
|
||||
y: 7
|
||||
anchors.right: parent.right
|
||||
MouseArea {
|
||||
onDoubleClicked: peerWindow.visible = true
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
Label {
|
||||
id: peerLabel
|
||||
font.pixelSize: 8
|
||||
text: "0 / 0"
|
||||
}
|
||||
Image {
|
||||
id: peerImage
|
||||
width: 10; height: 10
|
||||
source: "../network.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
property var blockModel: ListModel {
|
||||
id: blockModel
|
||||
}
|
||||
|
||||
SplitView {
|
||||
property var views: [];
|
||||
|
||||
id: mainSplit
|
||||
anchors.fill: parent
|
||||
resizing: false
|
||||
|
||||
function setView(view, menu) {
|
||||
for(var i = 0; i < views.length; i++) {
|
||||
views[i].view.visible = false
|
||||
views[i].menuItem.setSelection(false)
|
||||
}
|
||||
view.visible = true
|
||||
|
||||
//menu.border.color = "#CCCCCC"
|
||||
//menu.color = "#FFFFFFFF"
|
||||
menu.setSelection(true)
|
||||
}
|
||||
|
||||
function addComponent(view, options) {
|
||||
view.visible = false
|
||||
view.anchors.fill = mainView
|
||||
|
||||
var menuItem = menu.createMenuItem(view, options);
|
||||
if( view.hasOwnProperty("menuItem") ) {
|
||||
view.menuItem = menuItem;
|
||||
}
|
||||
|
||||
if( view.hasOwnProperty("onReady") ) {
|
||||
view.onReady.call(view)
|
||||
}
|
||||
|
||||
if( options.active ) {
|
||||
setView(view, menuItem)
|
||||
}
|
||||
|
||||
|
||||
return {view: view, menuItem: menuItem}
|
||||
}
|
||||
|
||||
/*********************
|
||||
* Main menu.
|
||||
********************/
|
||||
Rectangle {
|
||||
id: menu
|
||||
Layout.minimumWidth: 210
|
||||
Layout.maximumWidth: 210
|
||||
anchors.top: parent.top
|
||||
color: "#ececec"
|
||||
|
||||
Component {
|
||||
id: menuItemTemplate
|
||||
Rectangle {
|
||||
id: menuItem
|
||||
property var view;
|
||||
property var path;
|
||||
property var closable;
|
||||
|
||||
property alias title: label.text
|
||||
property alias icon: icon.source
|
||||
property alias secondaryTitle: secondary.text
|
||||
function setSelection(on) {
|
||||
sel.visible = on
|
||||
}
|
||||
|
||||
width: 206
|
||||
height: 28
|
||||
color: "#00000000"
|
||||
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: 4
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: sel
|
||||
visible: false
|
||||
anchors.fill: parent
|
||||
color: "#00000000"
|
||||
Rectangle {
|
||||
id: r
|
||||
anchors.fill: parent
|
||||
border.color: "#CCCCCC"
|
||||
border.width: 1
|
||||
radius: 5
|
||||
color: "#FFFFFFFF"
|
||||
}
|
||||
Rectangle {
|
||||
anchors {
|
||||
top: r.top
|
||||
bottom: r.bottom
|
||||
right: r.right
|
||||
}
|
||||
width: 10
|
||||
color: "#FFFFFFFF"
|
||||
|
||||
Rectangle {
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
}
|
||||
height: 1
|
||||
color: "#CCCCCC"
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
height: 1
|
||||
color: "#CCCCCC"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.closable) { return; }
|
||||
|
||||
if(this.view.hasOwnProperty("onDestroy")) {
|
||||
this.view.onDestroy.call(this.view)
|
||||
}
|
||||
|
||||
this.view.destroy()
|
||||
this.destroy()
|
||||
gui.removePlugin(this.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createMenuItem(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
|
||||
|
||||
if(view.hasOwnProperty("iconSource")) {
|
||||
comp.icon = view.iconSource;
|
||||
}
|
||||
comp.closable = options.close;
|
||||
|
||||
return comp
|
||||
}
|
||||
|
||||
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 {
|
||||
anchors.right: parent.right
|
||||
anchors.left: menu.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: parent.top
|
||||
color: "#00000000"
|
||||
|
||||
Rectangle {
|
||||
id: urlPane
|
||||
height: 40
|
||||
color: "#00000000"
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
leftMargin: 5
|
||||
rightMargin: 5
|
||||
top: parent.top
|
||||
topMargin: 5
|
||||
}
|
||||
TextField {
|
||||
id: url
|
||||
objectName: "url"
|
||||
placeholderText: "DApp URL"
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
topMargin: 5
|
||||
rightMargin: 5
|
||||
leftMargin: 5
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
if(/^https?/.test(this.text)) {
|
||||
root.browser.view.open(this.text);
|
||||
mainSplit.setView(root.browser.view, root.browser.menuItem);
|
||||
} else {
|
||||
addPlugin(this.text, {close: true, section: "apps"})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Border
|
||||
Rectangle {
|
||||
id: divider
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: urlPane.bottom
|
||||
}
|
||||
z: -1
|
||||
height: 1
|
||||
color: "#CCCCCC"
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: mainView
|
||||
color: "#00000000"
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: divider.bottom
|
||||
|
||||
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, {close: 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, caps: peer.caps})
|
||||
}
|
||||
|
||||
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" }
|
||||
TableViewColumn{width: 80; role: "caps" ; title: "Capabilities" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 30
|
||||
font.pointSize: 12
|
||||
text: "<h2>Mist (0.6.5)</h2><h4>Amalthea</h4><br><h3>Development</h3>Jeffrey Wilcke<br>Viktor Trón<br><h3>Building</h3>Maran Hidskes"
|
||||
}
|
||||
}
|
||||
|
||||
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: 300
|
||||
maximumWidth: 300
|
||||
maximumHeight: 50
|
||||
minimumHeight: 50
|
||||
title: "Connect to peer"
|
||||
|
||||
ComboBox {
|
||||
id: addrField
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.right: addPeerButton.left
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
onAccepted: {
|
||||
eth.connectToPeer(addrField.currentText)
|
||||
addPeerWin.visible = false
|
||||
}
|
||||
|
||||
editable: true
|
||||
model: ListModel { id: pastPeers }
|
||||
|
||||
Component.onCompleted: {
|
||||
var ips = eth.pastPeers()
|
||||
for(var i = 0; i < ips.length; i++) {
|
||||
pastPeers.append({text: ips.get(i)})
|
||||
}
|
||||
|
||||
pastPeers.insert(0, {text: "poc-7.ethdev.com:30303"})
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: addPeerButton
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.rightMargin: 10
|
||||
text: "Add"
|
||||
onClicked: {
|
||||
eth.connectToPeer(addrField.currentText)
|
||||
addPeerWin.visible = false
|
||||
}
|
||||
}
|
||||
Component.onCompleted: {
|
||||
addrField.focus = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import QtQuick 2.0
|
||||
import QtWebKit 3.0
|
||||
import QtWebKit.experimental 1.0
|
||||
import QtQuick.Controls 1.0;
|
||||
import QtQuick.Layouts 1.0;
|
||||
import QtQuick.Window 2.1;
|
||||
import Ethereum 1.0
|
||||
|
||||
ApplicationWindow {
|
||||
id: window
|
||||
title: "muted"
|
||||
width: 900
|
||||
height: 600
|
||||
minimumHeight: 300
|
||||
|
||||
property alias url: webView.url
|
||||
property alias webView: webView
|
||||
|
||||
|
||||
Item {
|
||||
id: root
|
||||
anchors.fill: parent
|
||||
WebView {
|
||||
objectName: "webView"
|
||||
id: webView
|
||||
anchors {
|
||||
top: root.top
|
||||
right: root.right
|
||||
left: root.left
|
||||
bottom: root.bottom
|
||||
//bottom: sizeGrip.top
|
||||
}
|
||||
|
||||
experimental.preferences.javascriptEnabled: true
|
||||
experimental.preferences.navigatorQtObjectEnabled: true
|
||||
experimental.onMessageReceived: {
|
||||
var data = JSON.parse(message.data)
|
||||
|
||||
switch(data.call) {
|
||||
case "log":
|
||||
console.log.apply(this, data.args)
|
||||
break;
|
||||
}
|
||||
}
|
||||
function postData(seed, data) {
|
||||
webview.experimental.postMessage(JSON.stringify({data: data, _seed: seed}))
|
||||
}
|
||||
function postEvent(event, data) {
|
||||
webview.experimental.postMessage(JSON.stringify({data: data, _event: event}))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Rectangle {
|
||||
id: sizeGrip
|
||||
color: "gray"
|
||||
height: 5
|
||||
anchors {
|
||||
left: root.left
|
||||
right: root.right
|
||||
}
|
||||
y: Math.round(root.height * 2 / 3)
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
drag.target: sizeGrip
|
||||
drag.minimumY: 0
|
||||
drag.maximumY: root.height - sizeGrip.height
|
||||
drag.axis: Drag.YAxis
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Controls 1.0;
|
||||
import QtQuick.Layouts 1.0;
|
||||
import Ethereum 1.0
|
||||
|
||||
QmlApp {
|
||||
minimumWidth: 350
|
||||
maximumWidth: 350
|
||||
maximumHeight: 80
|
||||
minimumHeight: 80
|
||||
|
||||
title: "Generic Coin"
|
||||
|
||||
property string contractAddr: "f299f6c74515620e4c4cd8fe3d205b5c4f2e25c8"
|
||||
property string addr: "2ef47100e0787b915105fd5e3f4ff6752079d5cb"
|
||||
|
||||
Component.onCompleted: {
|
||||
eth.watch(contractAddr, addr)
|
||||
eth.watch(addr, contractAddr)
|
||||
setAmount()
|
||||
}
|
||||
|
||||
function onStorageChangeCb(storageObject) {
|
||||
setAmount()
|
||||
}
|
||||
|
||||
function setAmount(){
|
||||
var state = eth.getStateObject(contractAddr)
|
||||
var storage = state.getStorage(addr)
|
||||
amountLabel.text = storage
|
||||
}
|
||||
Column {
|
||||
spacing: 5
|
||||
Row {
|
||||
spacing: 20
|
||||
Label {
|
||||
id: genLabel
|
||||
text: "Generic coin balance:"
|
||||
}
|
||||
Label {
|
||||
id: amountLabel
|
||||
}
|
||||
}
|
||||
Row {
|
||||
spacing: 20
|
||||
TextField {
|
||||
id: address
|
||||
placeholderText: "Address"
|
||||
}
|
||||
TextField {
|
||||
id: amount
|
||||
placeholderText: "Amount"
|
||||
}
|
||||
}
|
||||
Button {
|
||||
text: "Send coins"
|
||||
onClicked: {
|
||||
var privKey = eth.getKey().privateKey
|
||||
if(privKey){
|
||||
var result = eth.transact(privKey, contractAddr, 0,"100000","250", "0x" + address.text + "\n" + amount.text)
|
||||
resultTx.text = result.hash
|
||||
}
|
||||
}
|
||||
}
|
||||
Label {
|
||||
id: resultTx
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Controls 1.0;
|
||||
import QtQuick.Layouts 1.0;
|
||||
|
||||
Rectangle {
|
||||
id: transactionView
|
||||
visible: false
|
||||
Text { text: "TX VIEW" }
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Controls 1.0;
|
||||
import QtQuick.Layouts 1.0;
|
||||
import QtQuick.Dialogs 1.0;
|
||||
import QtQuick.Window 2.1;
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
import Ethereum 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
property var title: "Block Chain"
|
||||
property var menuItem
|
||||
|
||||
objectName: "chainView"
|
||||
visible: false
|
||||
anchors.fill: parent
|
||||
|
||||
TableView {
|
||||
id: blockTable
|
||||
width: parent.width
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
TableViewColumn{ role: "number" ; title: "#" ; width: 100 }
|
||||
TableViewColumn{ role: "hash" ; title: "Hash" ; width: 560 }
|
||||
TableViewColumn{ role: "txAmount" ; title: "Tx amount" ; width: 100 }
|
||||
|
||||
model: blockModel
|
||||
|
||||
itemDelegate: Item {
|
||||
Text {
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
leftMargin: 10
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
color: styleData.textColor
|
||||
elide: styleData.elideMode
|
||||
text: styleData.value
|
||||
font.pixelSize: 11
|
||||
MouseArea {
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
propagateComposedEvents: true
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
blockTable.selection.clear()
|
||||
blockTable.selection.select(styleData.row)
|
||||
|
||||
if(mouse.button == Qt.RightButton) {
|
||||
contextMenu.row = styleData.row;
|
||||
contextMenu.popup()
|
||||
}
|
||||
}
|
||||
|
||||
onDoubleClicked: {
|
||||
popup.visible = true
|
||||
popup.setDetails(blockModel.get(styleData.row))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Menu {
|
||||
id: contextMenu
|
||||
property var row
|
||||
MenuItem {
|
||||
text: "Details"
|
||||
onTriggered: {
|
||||
popup.visible = true
|
||||
popup.setDetails(blockModel.get(contextMenu.row))
|
||||
}
|
||||
}
|
||||
|
||||
MenuSeparator{}
|
||||
|
||||
MenuItem {
|
||||
text: "Copy"
|
||||
onTriggered: {
|
||||
copyToClipboard(blockModel.get(contextMenu.row).hash)
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: "Dump State"
|
||||
onTriggered: {
|
||||
generalFileDialog.show(false, function(path) {
|
||||
var hash = blockModel.get(contextMenu.row).hash;
|
||||
|
||||
gui.dumpState(hash, path);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function addBlock(block, initial) {
|
||||
if(initial == undefined){
|
||||
initial = false
|
||||
}
|
||||
|
||||
var amount = block.transactions.length;
|
||||
var txs = [];
|
||||
for(var i = 0; i < block.transactions.length; i++) {
|
||||
var tx = JSON.parse(block.transactions.getAsJson(i));
|
||||
txs.push(tx);
|
||||
}
|
||||
|
||||
if(initial){
|
||||
blockModel.append({size: block.size, number: block.number, name: block.name, gasLimit: block.gasLimit, gasUsed: block.gasUsed, coinbase: block.coinbase, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)})
|
||||
} else {
|
||||
blockModel.insert(0, {size: block.size, number: block.number, name: block.name, gasLimit: block.gasLimit, gasUsed: block.gasUsed, coinbase: block.coinbase, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)})
|
||||
}
|
||||
}
|
||||
|
||||
Window {
|
||||
id: popup
|
||||
visible: false
|
||||
//flags: Qt.CustomizeWindowHint | Qt.Tool | Qt.WindowCloseButtonHint
|
||||
property var block
|
||||
width: root.width
|
||||
height: 300
|
||||
Component{
|
||||
id: blockDetailsDelegate
|
||||
Rectangle {
|
||||
color: "#252525"
|
||||
width: popup.width
|
||||
height: 150
|
||||
Column {
|
||||
anchors.leftMargin: 10
|
||||
anchors.topMargin: 5
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
Text { text: '<h3>Block details</h3>'; color: "#F2F2F2"}
|
||||
Text { text: '<b>Block number:</b> ' + number + " (Size: " + size + ")"; color: "#F2F2F2"}
|
||||
Text { text: '<b>Hash:</b> ' + hash; color: "#F2F2F2"}
|
||||
Text { text: '<b>Coinbase:</b> <' + name + '> ' + coinbase; color: "#F2F2F2"}
|
||||
Text { text: '<b>Block found at:</b> ' + prettyTime; color: "#F2F2F2"}
|
||||
Text { text: '<b>Gas used:</b> ' + gasUsed + " / " + gasLimit; color: "#F2F2F2"}
|
||||
}
|
||||
}
|
||||
}
|
||||
ListView {
|
||||
model: singleBlock
|
||||
delegate: blockDetailsDelegate
|
||||
anchors.top: parent.top
|
||||
height: 100
|
||||
anchors.leftMargin: 20
|
||||
id: listViewThing
|
||||
Layout.maximumHeight: 40
|
||||
}
|
||||
TableView {
|
||||
id: txView
|
||||
anchors.top: listViewThing.bottom
|
||||
anchors.topMargin: 50
|
||||
width: parent.width
|
||||
|
||||
TableViewColumn{width: 90; role: "value" ; title: "Value" }
|
||||
TableViewColumn{width: 200; role: "hash" ; title: "Hash" }
|
||||
TableViewColumn{width: 200; role: "sender" ; title: "Sender" }
|
||||
TableViewColumn{width: 200;role: "address" ; title: "Receiver" }
|
||||
TableViewColumn{width: 60; role: "gas" ; title: "Gas" }
|
||||
TableViewColumn{width: 60; role: "gasPrice" ; title: "Gas Price" }
|
||||
TableViewColumn{width: 60; role: "isContract" ; title: "Contract" }
|
||||
|
||||
model: transactionModel
|
||||
onClicked: {
|
||||
var tx = transactionModel.get(row)
|
||||
if(tx.data) {
|
||||
popup.showContractData(tx)
|
||||
}else{
|
||||
popup.height = 440
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showContractData(tx) {
|
||||
txDetailsDebugButton.tx = tx
|
||||
if(tx.createsContract) {
|
||||
contractData.text = tx.data
|
||||
contractLabel.text = "<h4> Transaction created contract " + tx.address + "</h4>"
|
||||
}else{
|
||||
contractLabel.text = "<h4> Transaction ran contract " + tx.address + "</h4>"
|
||||
contractData.text = tx.rawData
|
||||
}
|
||||
popup.height = 540
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: txDetails
|
||||
width: popup.width
|
||||
height: 300
|
||||
anchors.left: listViewThing.left
|
||||
anchors.top: txView.bottom
|
||||
Label {
|
||||
text: "<h4>Contract data</h4>"
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
id: contractLabel
|
||||
anchors.leftMargin: 10
|
||||
}
|
||||
Button {
|
||||
property var tx
|
||||
id: txDetailsDebugButton
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 10
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 10
|
||||
text: "Debug contract"
|
||||
onClicked: {
|
||||
if(tx && tx.createsContract){
|
||||
eth.startDbWithCode(tx.rawData)
|
||||
}else {
|
||||
eth.startDbWithContractAndData(tx.address, tx.rawData)
|
||||
}
|
||||
}
|
||||
}
|
||||
TextArea {
|
||||
id: contractData
|
||||
text: "Contract"
|
||||
anchors.top: contractLabel.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.bottom: popup.bottom
|
||||
wrapMode: Text.Wrap
|
||||
width: parent.width - 30
|
||||
height: 80
|
||||
anchors.leftMargin: 10
|
||||
}
|
||||
}
|
||||
property var transactionModel: ListModel {
|
||||
id: transactionModel
|
||||
}
|
||||
property var singleBlock: ListModel {
|
||||
id: singleBlock
|
||||
}
|
||||
function setDetails(bl){
|
||||
singleBlock.set(0, bl)
|
||||
popup.height = 300
|
||||
transactionModel.clear()
|
||||
if(bl.txs !== undefined){
|
||||
for(var i = 0; i < bl.txs.count; i++) {
|
||||
transactionModel.insert(0, bl.txs.get(i))
|
||||
}
|
||||
if(bl.txs.count > 0 && bl.txs.get(0).data){
|
||||
popup.showContractData(bl.txs.get(0))
|
||||
}
|
||||
}
|
||||
txView.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Controls 1.0;
|
||||
import QtQuick.Layouts 1.0;
|
||||
import QtQuick.Dialogs 1.0;
|
||||
import QtQuick.Window 2.1;
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
import Ethereum 1.0
|
||||
|
||||
Rectangle {
|
||||
property var title: "Transactions"
|
||||
property var menuItem
|
||||
|
||||
|
||||
id: historyView
|
||||
visible: false
|
||||
anchors.fill: parent
|
||||
objectName: "transactionView"
|
||||
|
||||
property var txModel: ListModel {
|
||||
id: txModel
|
||||
}
|
||||
TableView {
|
||||
id: txTableView
|
||||
anchors.fill: parent
|
||||
TableViewColumn{ role: "inout" ; title: "" ; width: 40 }
|
||||
TableViewColumn{ role: "value" ; title: "Value" ; width: 100 }
|
||||
TableViewColumn{ role: "address" ; title: "Address" ; width: 430 }
|
||||
TableViewColumn{ role: "contract" ; title: "Contract" ; width: 100 }
|
||||
|
||||
model: txModel
|
||||
}
|
||||
|
||||
function addTx(tx, inout) {
|
||||
var isContract
|
||||
if (tx.contract == true){
|
||||
isContract = "Yes"
|
||||
}else{
|
||||
isContract = "No"
|
||||
}
|
||||
|
||||
|
||||
var address;
|
||||
if(inout == "recv") {
|
||||
address = tx.sender;
|
||||
} else {
|
||||
address = tx.address;
|
||||
}
|
||||
|
||||
txModel.insert(0, {inout: inout, hash: tx.hash, address: address, value: tx.value, contract: isContract})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Controls 1.0;
|
||||
import QtQuick.Layouts 1.0;
|
||||
import QtQuick.Dialogs 1.0;
|
||||
import QtQuick.Window 2.1;
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
import Ethereum 1.0
|
||||
|
||||
Rectangle {
|
||||
property var title: "Debug Info"
|
||||
property var menuItem
|
||||
|
||||
objectName: "infoView"
|
||||
visible: false
|
||||
anchors.fill: parent
|
||||
|
||||
color: "#00000000"
|
||||
|
||||
Column {
|
||||
id: info
|
||||
spacing: 3
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: 5
|
||||
anchors.leftMargin: 5
|
||||
|
||||
Label {
|
||||
id: addressLabel
|
||||
text: "Address"
|
||||
}
|
||||
TextField {
|
||||
text: eth.key().address
|
||||
width: 500
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Client ID"
|
||||
}
|
||||
TextField {
|
||||
text: gui.getCustomIdentifier()
|
||||
width: 500
|
||||
placeholderText: "Anonymous"
|
||||
onTextChanged: {
|
||||
gui.setCustomIdentifier(text)
|
||||
}
|
||||
}
|
||||
|
||||
TextArea {
|
||||
objectName: "statsPane"
|
||||
width: parent.width
|
||||
height: 200
|
||||
selectByMouse: true
|
||||
readOnly: true
|
||||
font.family: "Courier"
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: logLayout
|
||||
width: parent.width
|
||||
height: 200
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
TableView {
|
||||
id: addressView
|
||||
width: parent.width
|
||||
height: 200
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: logLevelSlider.left
|
||||
bottom: parent.bottom
|
||||
top: parent.top
|
||||
}
|
||||
TableViewColumn{ role: "name"; title: "name" }
|
||||
TableViewColumn{ role: "address"; title: "address"; width: 300}
|
||||
|
||||
property var addressModel: ListModel {
|
||||
id: addressModel
|
||||
}
|
||||
|
||||
model: addressModel
|
||||
itemDelegate: Item {
|
||||
Text {
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
leftMargin: 10
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
color: styleData.textColor
|
||||
elide: styleData.elideMode
|
||||
text: styleData.value
|
||||
font.pixelSize: 11
|
||||
MouseArea {
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
propagateComposedEvents: true
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
addressView.selection.clear()
|
||||
addressView.selection.select(styleData.row)
|
||||
|
||||
if(mouse.button == Qt.RightButton) {
|
||||
contextMenu.row = styleData.row;
|
||||
contextMenu.popup()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Menu {
|
||||
id: contextMenu
|
||||
property var row;
|
||||
|
||||
MenuItem {
|
||||
text: "Copy"
|
||||
onTriggered: {
|
||||
copyToClipboard(addressModel.get(this.row).address)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
TableView {
|
||||
id: logView
|
||||
headerVisible: false
|
||||
anchors {
|
||||
right: logLevelSlider.left
|
||||
left: parent.left
|
||||
bottom: parent.bottom
|
||||
top: parent.top
|
||||
}
|
||||
|
||||
TableViewColumn{ role: "description" ; title: "log" }
|
||||
|
||||
model: logModel
|
||||
}
|
||||
*/
|
||||
|
||||
Slider {
|
||||
id: logLevelSlider
|
||||
value: gui.getLogLevelInt()
|
||||
anchors {
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
|
||||
rightMargin: 5
|
||||
leftMargin: 5
|
||||
topMargin: 5
|
||||
bottomMargin: 5
|
||||
}
|
||||
|
||||
orientation: Qt.Vertical
|
||||
maximumValue: 5
|
||||
stepSize: 1
|
||||
|
||||
onValueChanged: {
|
||||
gui.setLogLevel(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property var logModel: ListModel {
|
||||
id: logModel
|
||||
}
|
||||
|
||||
function addDebugMessage(message){
|
||||
debuggerLog.append({value: message})
|
||||
}
|
||||
|
||||
function addAddress(address) {
|
||||
addressModel.append({name: address.name, address: address.address})
|
||||
}
|
||||
|
||||
function clearAddress() {
|
||||
addressModel.clear()
|
||||
}
|
||||
|
||||
function addLog(str) {
|
||||
// Remove first item once we've reached max log items
|
||||
if(logModel.count > 250) {
|
||||
logModel.remove(0)
|
||||
}
|
||||
|
||||
if(str.len != 0) {
|
||||
if(logView.flickableItem.atYEnd) {
|
||||
logModel.append({description: str})
|
||||
logView.positionViewAtRow(logView.rowCount - 1, ListView.Contain)
|
||||
} else {
|
||||
logModel.append({description: str})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 82 KiB |
@@ -0,0 +1,190 @@
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Controls 1.0;
|
||||
import QtQuick.Layouts 1.0;
|
||||
import QtQuick.Dialogs 1.0;
|
||||
import QtQuick.Window 2.1;
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
property var title: "JeffCoin"
|
||||
property var iconSource: "./views/jeffcoin/jeff.png"
|
||||
property var menuItem
|
||||
property var filter
|
||||
property var address: "fc0a9436890478bb9b1c6ed7455c2535366f4a99"
|
||||
|
||||
function insertTx(message, blockNumber) {
|
||||
if(!message) return;
|
||||
|
||||
var from = message.from
|
||||
var to = message.input.substr(24, 40)
|
||||
var value = eth.fromNumber(message.input.substr(64, 64))
|
||||
|
||||
var me = eth.key().address;
|
||||
if((to == me|| from == me) && message.input.length == 128) {
|
||||
var to = eth.lookupName(to)
|
||||
var from = eth.lookupName(from)
|
||||
txModel.insert(0, {confirmations: blockNumber - message.number, from: from, to: to, value: value})
|
||||
}
|
||||
}
|
||||
|
||||
function setBalance() {
|
||||
var jeffCoinAmount = eth.fromNumber(eth.storageAt(address, eth.key().address)) + " JΞF"
|
||||
menuItem.secondaryTitle = jeffCoinAmount
|
||||
|
||||
balance.text = "<b>Balance</b>: " + jeffCoinAmount;
|
||||
}
|
||||
|
||||
function onReady() {
|
||||
setBalance()
|
||||
|
||||
filter = new ethx.watch({latest: -1, to: address})
|
||||
filter.changed(function(messages) {
|
||||
setBalance()
|
||||
|
||||
var blockNumber = eth.block(-1).number;
|
||||
for(var i = 0; i < messages.length; i++) {
|
||||
insertTx(messages.get(i), blockNumber);
|
||||
}
|
||||
});
|
||||
|
||||
var blockNumber = eth.block(-1).number;
|
||||
var msgs = filter.messages()
|
||||
for(var i = msgs.length-1; i >= 0; i--) {
|
||||
var message = JSON.parse(msgs.getAsJson(i))
|
||||
|
||||
insertTx(message, blockNumber)
|
||||
}
|
||||
|
||||
var chainChanged = ethx.watch("chain")
|
||||
chainChanged.changed(function() {
|
||||
for(var i = 0; i < txModel.count; i++) {
|
||||
var entry = txModel.get(i);
|
||||
entry.confirmations++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onDestroy() {
|
||||
filter.uninstall()
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 10
|
||||
y: 40
|
||||
anchors.fill: parent
|
||||
|
||||
Text {
|
||||
id: balance
|
||||
text: "<b>Balance</b>: " + eth.fromNumber(eth.storageAt(address, eth.key().address)) + " JΞF"
|
||||
font.pixelSize: 24
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
top: parent.top
|
||||
topMargin: 20
|
||||
}
|
||||
}
|
||||
|
||||
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: "JΞF "
|
||||
}
|
||||
|
||||
// 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 lookup = eth.lookupAddress(address)
|
||||
if(lookup.length == 0)
|
||||
lookup = address
|
||||
|
||||
eth.transact({from: eth.key().privateKey, to:lookup, gas: "9000", gasPrice: "10000000000000", data: ["0x"+txTo.text, txValue.text]})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: newTxPane.bottom
|
||||
topMargin: 10
|
||||
bottom: parent.bottom
|
||||
}
|
||||
TableView {
|
||||
id: txTableView
|
||||
anchors.fill : parent
|
||||
TableViewColumn{ role: "value" ; title: "Amount" ; width: 100 }
|
||||
TableViewColumn{ role: "from" ; title: "From" ; width: 280 }
|
||||
TableViewColumn{ role: "to" ; title: "To" ; width: 280 }
|
||||
TableViewColumn{ role: "confirmations" ; title: "Confirmations" ; width: 100 }
|
||||
|
||||
model: ListModel {
|
||||
id: txModel
|
||||
Component.onCompleted: {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Controls 1.0;
|
||||
import QtQuick.Layouts 1.0;
|
||||
import QtQuick.Dialogs 1.0;
|
||||
import QtQuick.Window 2.1;
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
import Ethereum 1.0
|
||||
|
||||
Rectangle {
|
||||
property var title: "Pending Transactions"
|
||||
property var menuItem
|
||||
|
||||
objectName: "pendingTxView"
|
||||
anchors.fill: parent
|
||||
visible: false
|
||||
id: pendingTxView
|
||||
|
||||
property var pendingTxModel: ListModel {
|
||||
id: pendingTxModel
|
||||
}
|
||||
|
||||
TableView {
|
||||
id: pendingTxTableView
|
||||
anchors.fill: parent
|
||||
TableViewColumn{ role: "value" ; title: "Value" ; width: 100 }
|
||||
TableViewColumn{ role: "from" ; title: "sender" ; width: 230 }
|
||||
TableViewColumn{ role: "to" ; title: "Reciever" ; width: 230 }
|
||||
TableViewColumn{ role: "contract" ; title: "Contract" ; width: 100 }
|
||||
|
||||
model: pendingTxModel
|
||||
}
|
||||
|
||||
function addTx(tx, inout) {
|
||||
var isContract
|
||||
if (tx.contract == true){
|
||||
isContract = "Yes"
|
||||
}else{
|
||||
isContract = "No"
|
||||
}
|
||||
|
||||
|
||||
pendingTxModel.insert(0, {hash: tx.hash, to: tx.address, from: tx.sender, value: tx.value, contract: isContract})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Controls 1.0;
|
||||
import QtQuick.Layouts 1.0;
|
||||
import QtQuick.Dialogs 1.0;
|
||||
import QtQuick.Window 2.1;
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
import Ethereum 1.0
|
||||
|
||||
Rectangle {
|
||||
property var title: "New Transaction"
|
||||
property var menuItem
|
||||
|
||||
objectName: "newTxView"
|
||||
visible: false
|
||||
anchors.fill: parent
|
||||
color: "#00000000"
|
||||
|
||||
Column {
|
||||
id: mainContractColumn
|
||||
anchors.fill: parent
|
||||
|
||||
|
||||
states: [
|
||||
State{
|
||||
name: "ERROR"
|
||||
|
||||
PropertyChanges { target: txResult; visible:true}
|
||||
PropertyChanges { target: codeView; visible:true}
|
||||
},
|
||||
State {
|
||||
name: "DONE"
|
||||
|
||||
PropertyChanges { target: txValue; visible:false}
|
||||
PropertyChanges { target: txGas; visible:false}
|
||||
PropertyChanges { target: txGasPrice; visible:false}
|
||||
PropertyChanges { target: codeView; visible:false}
|
||||
PropertyChanges { target: txButton; visible:false}
|
||||
PropertyChanges { target: txDataLabel; visible:false}
|
||||
PropertyChanges { target: atLabel; visible:false}
|
||||
PropertyChanges { target: txFuelRecipient; visible:false}
|
||||
PropertyChanges { target: valueDenom; visible:false}
|
||||
PropertyChanges { target: gasDenom; visible:false}
|
||||
|
||||
PropertyChanges { target: txResult; visible:true}
|
||||
PropertyChanges { target: txOutput; visible:true}
|
||||
PropertyChanges { target: newTxButton; visible:true}
|
||||
},
|
||||
State {
|
||||
name: "SETUP"
|
||||
|
||||
PropertyChanges { target: txValue; visible:true; text: ""}
|
||||
PropertyChanges { target: txGas; visible:true;}
|
||||
PropertyChanges { target: txGasPrice; visible:true;}
|
||||
PropertyChanges { target: codeView; visible:true; text: ""}
|
||||
PropertyChanges { target: txButton; visible:true}
|
||||
PropertyChanges { target: txDataLabel; visible:true}
|
||||
PropertyChanges { target: valueDenom; visible:true}
|
||||
PropertyChanges { target: gasDenom; visible:true}
|
||||
|
||||
PropertyChanges { target: txResult; visible:false}
|
||||
PropertyChanges { target: txOutput; visible:false}
|
||||
PropertyChanges { target: newTxButton; visible:false}
|
||||
}
|
||||
]
|
||||
width: 400
|
||||
spacing: 5
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.leftMargin: 5
|
||||
anchors.topMargin: 5
|
||||
|
||||
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" }
|
||||
}
|
||||
|
||||
|
||||
TextField {
|
||||
id: txFuelRecipient
|
||||
placeholderText: "Address / Name or empty for contract"
|
||||
//validator: RegExpValidator { regExp: /[a-f0-9]{40}/ }
|
||||
width: 400
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
TextField {
|
||||
id: txValue
|
||||
width: 222
|
||||
placeholderText: "Amount"
|
||||
validator: RegExpValidator { regExp: /\d*/ }
|
||||
onTextChanged: {
|
||||
contractFormReady()
|
||||
}
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: valueDenom
|
||||
currentIndex: 6
|
||||
model: denomModel
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
TextField {
|
||||
id: txGas
|
||||
width: 50
|
||||
validator: RegExpValidator { regExp: /\d*/ }
|
||||
placeholderText: "Gas"
|
||||
text: "500"
|
||||
}
|
||||
Label {
|
||||
id: atLabel
|
||||
text: "@"
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: txGasPrice
|
||||
width: 200
|
||||
placeholderText: "Gas price"
|
||||
text: "10"
|
||||
validator: RegExpValidator { regExp: /\d*/ }
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: gasDenom
|
||||
currentIndex: 4
|
||||
model: denomModel
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
id: txDataLabel
|
||||
text: "Data"
|
||||
}
|
||||
|
||||
TextArea {
|
||||
id: codeView
|
||||
height: 300
|
||||
anchors.topMargin: 5
|
||||
width: 400
|
||||
onTextChanged: {
|
||||
contractFormReady()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Button {
|
||||
id: txButton
|
||||
/* enabled: false */
|
||||
states: [
|
||||
State {
|
||||
name: "READY"
|
||||
PropertyChanges { target: txButton; /*enabled: true*/}
|
||||
},
|
||||
State {
|
||||
name: "NOTREADY"
|
||||
PropertyChanges { target: txButton; /*enabled:false*/}
|
||||
}
|
||||
]
|
||||
text: "Send"
|
||||
onClicked: {
|
||||
var value = txValue.text + denomModel.get(valueDenom.currentIndex).zeros;
|
||||
var gasPrice = txGasPrice.text + denomModel.get(gasDenom.currentIndex).zeros;
|
||||
var res = gui.transact(txFuelRecipient.text, value, txGas.text, gasPrice, codeView.text)
|
||||
if(res[1]) {
|
||||
txResult.text = "Your contract <b>could not</b> be sent over the network:\n<b>"
|
||||
txResult.text += res[1].error()
|
||||
txResult.text += "</b>"
|
||||
mainContractColumn.state = "ERROR"
|
||||
} else {
|
||||
txResult.text = "Your transaction has been submitted:\n"
|
||||
txOutput.text = res[0].address
|
||||
mainContractColumn.state = "DONE"
|
||||
|
||||
console.log(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
Text {
|
||||
id: txResult
|
||||
visible: false
|
||||
}
|
||||
TextField {
|
||||
id: txOutput
|
||||
visible: false
|
||||
width: 530
|
||||
}
|
||||
Button {
|
||||
id: newTxButton
|
||||
visible: false
|
||||
text: "Create a new transaction"
|
||||
onClicked: {
|
||||
this.visible = false
|
||||
txResult.text = ""
|
||||
txOutput.text = ""
|
||||
mainContractColumn.state = "SETUP"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function contractFormReady(){
|
||||
if(codeView.text.length > 0 && txValue.text.length > 0 && txGas.text.length > 0 && txGasPrice.length > 0) {
|
||||
txButton.state = "READY"
|
||||
}else{
|
||||
txButton.state = "NOTREADY"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Controls 1.0;
|
||||
import QtQuick.Layouts 1.0;
|
||||
import QtQuick.Dialogs 1.0;
|
||||
import QtQuick.Window 2.1;
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
import Ethereum 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
property var title: "Wallet"
|
||||
property var iconSource: "../facet.png"
|
||||
property var menuItem
|
||||
|
||||
objectName: "walletView"
|
||||
anchors.fill: parent
|
||||
|
||||
function onReady() {
|
||||
menuItem.secondaryTitle = 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.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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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({from: eth.key().privateKey, to: txTo.text, value: value, gas: "500", gasPrice: 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 filter = ethx.watch({latest: -1, from: eth.key().address});
|
||||
filter.changed(addTxs)
|
||||
|
||||
addTxs(filter.messages())
|
||||
}
|
||||
|
||||
function addTxs(messages) {
|
||||
for(var i = 0; i < messages.length; i++) {
|
||||
var message = messages.get(i);
|
||||
var to = eth.lookupName(message.to);
|
||||
var from = eth.lookupName(message.from);
|
||||
txModel.insert(0, {num: txModel.count, from: from, to: to, value: eth.numberToHuman(message.value)})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,408 @@
|
||||
import QtQuick 2.0
|
||||
import QtWebKit 3.0
|
||||
import QtWebKit.experimental 1.0
|
||||
import QtQuick.Controls 1.0;
|
||||
import QtQuick.Controls.Styles 1.0
|
||||
import QtQuick.Layouts 1.0;
|
||||
import QtQuick.Window 2.1;
|
||||
import Ethereum 1.0
|
||||
|
||||
Rectangle {
|
||||
id: window
|
||||
property var title: "Browser"
|
||||
property var iconSource: "../browser.png"
|
||||
property var menuItem
|
||||
|
||||
property alias url: webview.url
|
||||
property alias webView: webview
|
||||
|
||||
property var cleanPath: false
|
||||
property var open: function(url) {
|
||||
if(!window.cleanPath) {
|
||||
var uri = url;
|
||||
if(!/.*\:\/\/.*/.test(uri)) {
|
||||
uri = "http://" + uri;
|
||||
}
|
||||
|
||||
var reg = /(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.eth)(.*)/
|
||||
|
||||
if(reg.test(uri)) {
|
||||
uri.replace(reg, function(match, pre, domain, path) {
|
||||
uri = pre;
|
||||
|
||||
var lookup = eth.lookupDomain(domain.substring(0, domain.length - 4));
|
||||
var ip = [];
|
||||
for(var i = 0, l = lookup.length; i < l; i++) {
|
||||
ip.push(lookup.charCodeAt(i))
|
||||
}
|
||||
|
||||
if(ip.length != 0) {
|
||||
uri += lookup;
|
||||
} else {
|
||||
uri += domain;
|
||||
}
|
||||
|
||||
uri += path;
|
||||
});
|
||||
}
|
||||
|
||||
window.cleanPath = true;
|
||||
|
||||
webview.url = uri;
|
||||
|
||||
//uriNav.text = uri.text.replace(/(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.\w{2,3})(.*)/, "$1$2<span style='color:#CCC'>$3</span>");
|
||||
uriNav.text = uri;
|
||||
} else {
|
||||
// Prevent inf loop.
|
||||
window.cleanPath = false;
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
webview.url = "http://etherian.io"
|
||||
}
|
||||
|
||||
signal messages(var messages, int id);
|
||||
onMessages: {
|
||||
// Bit of a cheat to get proper JSON
|
||||
var m = JSON.parse(JSON.parse(JSON.stringify(messages)))
|
||||
webview.postEvent("messages", [m, id]);
|
||||
}
|
||||
|
||||
Item {
|
||||
objectName: "root"
|
||||
id: root
|
||||
anchors.fill: parent
|
||||
state: "inspectorShown"
|
||||
|
||||
RowLayout {
|
||||
id: navBar
|
||||
height: 40
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
leftMargin: 7
|
||||
}
|
||||
|
||||
Button {
|
||||
id: back
|
||||
onClicked: {
|
||||
webview.goBack()
|
||||
}
|
||||
style: ButtonStyle {
|
||||
background: Image {
|
||||
source: "../back.png"
|
||||
width: 30
|
||||
height: 30
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextField {
|
||||
anchors {
|
||||
left: back.right
|
||||
right: toggleInspector.left
|
||||
leftMargin: 5
|
||||
rightMargin: 5
|
||||
}
|
||||
text: "http://etherian.io"
|
||||
id: uriNav
|
||||
y: parent.height / 2 - this.height / 2
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
webview.url = this.text;
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: toggleInspector
|
||||
anchors {
|
||||
right: parent.right
|
||||
}
|
||||
iconSource: "../bug.png"
|
||||
onClicked: {
|
||||
if(inspector.visible == true){
|
||||
inspector.visible = false
|
||||
}else{
|
||||
inspector.visible = true
|
||||
inspector.url = webview.experimental.remoteInspectorUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
WebView {
|
||||
objectName: "webView"
|
||||
id: webview
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
top: navBar.bottom
|
||||
}
|
||||
|
||||
//property var cleanPath: false
|
||||
onNavigationRequested: {
|
||||
window.open(request.url.toString());
|
||||
}
|
||||
|
||||
function sendMessage(data) {
|
||||
webview.experimental.postMessage(JSON.stringify(data))
|
||||
}
|
||||
|
||||
|
||||
experimental.preferences.javascriptEnabled: true
|
||||
experimental.preferences.navigatorQtObjectEnabled: true
|
||||
experimental.preferences.developerExtrasEnabled: true
|
||||
experimental.userScripts: ["../ext/qt_messaging_adapter.js", "../ext/q.js", "../ext/big.js", "../ext/string.js", "../ext/html_messaging.js"]
|
||||
experimental.onMessageReceived: {
|
||||
console.log("[onMessageReceived]: ", message.data)
|
||||
// TODO move to messaging.js
|
||||
var data = JSON.parse(message.data)
|
||||
|
||||
try {
|
||||
switch(data.call) {
|
||||
case "compile":
|
||||
postData(data._seed, eth.compile(data.args[0]))
|
||||
break
|
||||
|
||||
case "getCoinBase":
|
||||
postData(data._seed, eth.coinBase())
|
||||
|
||||
break
|
||||
|
||||
case "getIsListening":
|
||||
postData(data._seed, eth.isListening())
|
||||
|
||||
break
|
||||
|
||||
case "getIsMining":
|
||||
postData(data._seed, eth.isMining())
|
||||
|
||||
break
|
||||
|
||||
case "getPeerCount":
|
||||
postData(data._seed, eth.peerCount())
|
||||
|
||||
break
|
||||
|
||||
case "getCountAt":
|
||||
require(1)
|
||||
postData(data._seed, eth.txCountAt(data.args[0]))
|
||||
|
||||
break
|
||||
|
||||
case "getCodeAt":
|
||||
require(1)
|
||||
var code = eth.codeAt(data.args[0])
|
||||
postData(data._seed, code);
|
||||
|
||||
break
|
||||
|
||||
case "getBlockByNumber":
|
||||
var block = eth.blockByNumber(data.args[0])
|
||||
postData(data._seed, block)
|
||||
|
||||
break
|
||||
|
||||
case "getBlockByHash":
|
||||
var block = eth.blockByHash(data.args[0])
|
||||
postData(data._seed, block)
|
||||
|
||||
break
|
||||
|
||||
case "transact":
|
||||
require(5)
|
||||
|
||||
var tx = eth.transact(data.args)
|
||||
postData(data._seed, tx)
|
||||
|
||||
break
|
||||
|
||||
case "getStorageAt":
|
||||
require(2);
|
||||
|
||||
var storage = eth.storageAt(data.args[0], data.args[1]);
|
||||
postData(data._seed, storage)
|
||||
|
||||
break
|
||||
|
||||
case "call":
|
||||
require(1);
|
||||
var ret = eth.call(data.args)
|
||||
postData(data._seed, ret)
|
||||
|
||||
break
|
||||
|
||||
case "getEachStorage":
|
||||
require(1);
|
||||
var storage = JSON.parse(eth.eachStorage(data.args[0]))
|
||||
postData(data._seed, storage)
|
||||
|
||||
break
|
||||
|
||||
case "getTransactionsFor":
|
||||
require(1);
|
||||
var txs = eth.transactionsFor(data.args[0], true)
|
||||
postData(data._seed, txs)
|
||||
|
||||
break
|
||||
|
||||
case "getBalanceAt":
|
||||
require(1);
|
||||
|
||||
postData(data._seed, eth.balanceAt(data.args[0]));
|
||||
|
||||
break
|
||||
|
||||
case "getKey":
|
||||
var key = eth.key().privateKey;
|
||||
|
||||
postData(data._seed, key)
|
||||
break
|
||||
|
||||
case "watch":
|
||||
require(2)
|
||||
eth.watch(data.args[0], data.args[1])
|
||||
|
||||
case "disconnect":
|
||||
require(1)
|
||||
postData(data._seed, null)
|
||||
|
||||
break;
|
||||
|
||||
case "getSecretToAddress":
|
||||
require(1)
|
||||
|
||||
var addr = eth.secretToAddress(data.args[0])
|
||||
console.log("getsecret", addr)
|
||||
postData(data._seed, addr)
|
||||
|
||||
break;
|
||||
|
||||
case "messages":
|
||||
require(1);
|
||||
|
||||
var messages = JSON.parse(eth.getMessages(data.args[0]))
|
||||
postData(data._seed, messages)
|
||||
|
||||
break
|
||||
|
||||
case "mutan":
|
||||
require(1)
|
||||
|
||||
var code = eth.compileMutan(data.args[0])
|
||||
postData(data._seed, "0x"+code)
|
||||
|
||||
break;
|
||||
|
||||
case "newFilterString":
|
||||
require(1)
|
||||
var id = eth.newFilterString(data.args[0])
|
||||
postData(data._seed, id);
|
||||
break;
|
||||
case "newFilter":
|
||||
require(1)
|
||||
var id = eth.newFilter(data.args[0])
|
||||
|
||||
postData(data._seed, id);
|
||||
break;
|
||||
|
||||
case "getMessages":
|
||||
require(1);
|
||||
|
||||
var messages = eth.messages(data.args[0]);
|
||||
var m = JSON.parse(JSON.parse(JSON.stringify(messages)))
|
||||
postData(data._seed, m);
|
||||
|
||||
break;
|
||||
|
||||
case "deleteFilter":
|
||||
require(1);
|
||||
eth.uninstallFilter(data.args[0])
|
||||
break;
|
||||
}
|
||||
} catch(e) {
|
||||
console.log(data.call + ": " + e)
|
||||
|
||||
postData(data._seed, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function post(seed, data) {
|
||||
postData(data._seed, data)
|
||||
}
|
||||
|
||||
function require(args, num) {
|
||||
if(args.length < num) {
|
||||
throw("required argument count of "+num+" got "+args.length);
|
||||
}
|
||||
}
|
||||
function postData(seed, data) {
|
||||
webview.experimental.postMessage(JSON.stringify({data: data, _seed: seed}))
|
||||
}
|
||||
function postEvent(event, data) {
|
||||
webview.experimental.postMessage(JSON.stringify({data: data, _event: event}))
|
||||
}
|
||||
|
||||
function onWatchedCb(data, id) {
|
||||
var messages = JSON.parse(data)
|
||||
postEvent("watched:"+id, messages)
|
||||
}
|
||||
|
||||
function onNewBlockCb(block) {
|
||||
postEvent("block:new", block)
|
||||
}
|
||||
function onObjectChangeCb(stateObject) {
|
||||
postEvent("object:"+stateObject.address(), stateObject)
|
||||
}
|
||||
function onStorageChangeCb(storageObject) {
|
||||
var ev = ["storage", storageObject.stateAddress, storageObject.address].join(":");
|
||||
postEvent(ev, [storageObject.address, storageObject.value])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: sizeGrip
|
||||
color: "gray"
|
||||
visible: false
|
||||
height: 10
|
||||
anchors {
|
||||
left: root.left
|
||||
right: root.right
|
||||
}
|
||||
y: Math.round(root.height * 2 / 3)
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
drag.target: sizeGrip
|
||||
drag.minimumY: 0
|
||||
drag.maximumY: root.height
|
||||
drag.axis: Drag.YAxis
|
||||
}
|
||||
}
|
||||
|
||||
WebView {
|
||||
id: inspector
|
||||
visible: false
|
||||
anchors {
|
||||
left: root.left
|
||||
right: root.right
|
||||
top: sizeGrip.bottom
|
||||
bottom: root.bottom
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "inspectorShown"
|
||||
PropertyChanges {
|
||||
target: inspector
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user