@ -7,12 +7,13 @@ import QtQuick.Controls.Styles 1.3
import org . ethereum . qml . QEther 1.0
import "js/TransactionHelper.js" as TransactionHelper
import "js/InputValidator.js" as InputValidator
import "js/NetworkDeployment.js" as NetworkDeployment
import "."
Dialog {
id: modalTransactionDialog
modality: Qt . ApplicationModal
width: 57 0
width: 58 0
height: 500
visible: false
title: qsTr ( "Edit Transaction" )
@ -22,7 +23,7 @@ Dialog {
property alias gasAuto: gasAutoCheck . checked ;
property alias gasPrice: gasPriceField . value ;
property alias transactionValue: valueField . value ;
property string contractId: contractComboBox . currentValue ( ) ;
property string contractId: contractCreationC omboBox . currentValue ( ) ;
property alias functionId: functionComboBox . currentText ;
property var paramValues ;
property var paramsModel: [ ] ;
@ -30,21 +31,16 @@ Dialog {
property alias stateAccounts: senderComboBox . model
property bool saveStatus
signal accepted ;
property int rowWidth: 500
StateDialogStyle {
id: transactionDialogStyle
}
function open ( index , blockIdx , item ) {
rowFunction . visible = ! useTransactionDefaultValue ;
rowValue . visible = ! useTransactionDefaultValue ;
rowGas . visible = ! useTransactionDefaultValue ;
rowGasPrice . visible = ! useTransactionDefaultValue ;
transactionIndex = index
blockIndex = blockIdx
typeLoader . transactionIndex = index
typeLoader . blockIndex = blockIdx
paramScroll . transactionIndex = index
paramScroll . blockIndex = blockIdx
saveStatus = item . saveStatus
gasValueEdit . gasValue = item . gas ;
gasAutoCheck . checked = item . gasAuto ? true : false ;
@ -52,48 +48,20 @@ Dialog {
valueField . value = item . value ;
var contractId = item . contractId ;
var functionId = item . functionId ;
rowFunction . visible = true ;
paramValues = item . parameters !== undefined ? item.parameters : { } ;
if ( item . sender )
senderComboBox . select ( item . sender ) ;
contractsModel . clear ( ) ;
var contractIndex = - 1 ;
var contracts = codeModel . contracts ;
for ( var c in contracts ) {
contractsModel . append ( { cid: c , text: contracts [ c ] . contract . name } ) ;
if ( contracts [ c ] . contract . name === contractId )
contractIndex = contractsModel . count - 1 ;
}
if ( contractIndex == - 1 && contractsModel . count > 0 )
contractIndex = 0 ; / / @ t o d o s u g g e s t u n u s e d c o n t r a c t
contractComboBox . currentIndex = contractIndex ;
trTypeCreate . checked = item . isContractCreation
trTypeSend . checked = ! item . isFunctionCall
trTypeExecute . checked = item . isFunctionCall && ! item . isContractCreation
recipients . accounts = senderComboBox . model ;
recipients . subType = "address" ;
recipients . load ( ) ;
recipients . init ( ) ;
recipients . select ( contractId ) ;
if ( item . isContractCreation )
loadFunctions ( contractComboBox . currentValue ( ) ) ;
else
loadFunctions ( contractFromToken ( recipients . currentValue ( ) ) )
selectFunction ( functionId ) ;
trType . checked = item . isContractCreation
trType . init ( ) ;
paramsModel = [ ] ;
if ( item . isContractCreation )
loadCtorParameters ( ) ;
else
loadParameters ( ) ;
load ( item . isContractCreation , item . isFunctionCall , functionId , contractId )
estimatedGas . updateView ( )
visible = true ;
valueField . focus = true ;
}
function loadCtorParameters ( contractId )
@ -111,12 +79,12 @@ Dialog {
function loadFunctions ( contractId )
{
functionsModel . clear ( ) ;
functionsModel . append ( { text: " - " } ) ;
var contract = codeModel . contracts [ contractId ] ;
if ( contract ) {
var functions = codeModel . contracts [ contractId ] . contract . functions ;
for ( var f = 0 ; f < functions . length ; f ++ ) {
functionsModel . append ( { text: functions [ f ] . name } ) ;
if ( functions [ f ] . name !== contractId )
functionsModel . append ( { text: functions [ f ] . name } ) ;
}
}
}
@ -156,9 +124,9 @@ Dialog {
function loadParameters ( ) {
paramsModel = [ ]
if ( functionComboBox . currentIndex >= 0 && functionComboBox . currentIndex < functionsModel . count ) {
var contract = codeModel . contracts [ contractFromToken ( recipients . currentValue ( ) ) ] ;
var contract = codeModel . contracts [ contractFromToken ( contractCreationComboBox . currentValue ( ) ) ] ;
if ( contract ) {
var func = contract . contract . functions [ functionComboBox . currentIndex - 1 ] ;
var func = contract . contract . functions [ functionComboBox . currentIndex + 1 ] ;
if ( func ) {
var parameters = func . parameters ;
for ( var p = 0 ; p < parameters . length ; p ++ )
@ -171,13 +139,11 @@ Dialog {
function initTypeLoader ( )
{
typeLoader . value = { }
typeLoader . members = [ ]
typeLoader . value = paramValues ;
typeLoader . members = paramsModel ;
paramLabel . visible = paramsModel . length > 0 ;
paramScroll . visible = paramsModel . length > 0 ;
modalTransactionDialog . height = ( paramsModel . length > 0 ? 500 : 300 ) ;
paramScroll . value = { }
paramScroll . members = [ ]
paramScroll . value = paramValues ;
paramScroll . members = paramsModel ;
paramScroll . updateView ( )
}
function acceptAndClose ( )
@ -213,16 +179,16 @@ Dialog {
item . functionId = transactionDialog . functionId ;
}
item . isContractCreation = trType . checked ;
item . isContractCreation = trTypeCreate . checked ;
if ( item . isContractCreation )
item . functionId = item . contractId ;
item . isFunctionCall = item . functionId !== " - " ;
item . isFunctionCall = trTypeExecute . checked
if ( ! item . isContractCreation )
{
item . contractId = recipients . currentText ;
item . label = item . contractId + " " + item . functionId ;
if ( recipients . current ( ) . type === "address" )
item . contractId = recipientsAccount . currentValue ( ) ;
item . label = contractFromToken ( item . contractId ) + ". " + item . functionId + "()" ;
if ( recipientsAccount . current ( ) . type === "address" )
{
item . functionId = "" ;
item . isFunctionCall = false ;
@ -230,8 +196,9 @@ Dialog {
}
else
{
item . isFunctionCall = true
item . functionId = item . contractId ;
item . label = qsTr ( "Deploy" ) + " " + item . contractId ;
item . label = item . contractId + ". " + item . contractId + "()" ;
}
item . saveStatus = saveStatus
item . sender = senderComboBox . model [ senderComboBox . currentIndex ] . secret ;
@ -246,187 +213,360 @@ Dialog {
return token ;
}
function load ( isContractCreation , isFunctionCall , functionId , contractId )
{
if ( ! isContractCreation )
{
contractCreationComboBox . visible = false
recipientsAccount . visible = true
recipientsAccount . accounts = senderComboBox . model ;
amountLabel . text = qsTr ( "Amount" )
if ( ! isFunctionCall )
recipientsAccount . subType = "address"
else
recipientsAccount . subType = "contract" ;
recipientsAccount . load ( ) ;
recipientsAccount . init ( ) ;
if ( contractId )
recipientsAccount . select ( contractId ) ;
if ( functionId )
selectFunction ( functionId ) ;
if ( isFunctionCall )
{
labelRecipient . text = qsTr ( "Recipient Contract" )
functionRect . show ( )
loadFunctions ( contractFromToken ( recipientsAccount . currentValue ( ) ) )
loadParameters ( ) ;
paramScroll . updateView ( )
}
else
{
paramsModel = [ ]
paramScroll . updateView ( )
labelRecipient . text = qsTr ( "Recipient Account" )
functionRect . hide ( )
}
}
else
{
/ / c o n t r a c t c r e a t i o n
contractsModel . clear ( ) ;
var contractIndex = - 1 ;
var contracts = codeModel . contracts ;
for ( var c in contracts ) {
contractsModel . append ( { cid: c , text: contracts [ c ] . contract . name } ) ;
if ( contracts [ c ] . contract . name === contractId )
contractIndex = contractsModel . count - 1 ;
}
if ( contractIndex == - 1 && contractsModel . count > 0 )
contractIndex = 0 ; / / @ t o d o s u g g e s t u n u s e d c o n t r a c t
contractCreationComboBox . currentIndex = contractIndex ;
contractCreationComboBox . visible = true
labelRecipient . text = qsTr ( "Contract" )
amountLabel . text = qsTr ( "Endownment" )
functionRect . hide ( )
recipientsAccount . visible = false
loadCtorParameters ( contractCreationComboBox . currentValue ( ) ) ;
paramScroll . updateView ( )
}
}
contentItem: Rectangle {
id: containerRect
color: transactionDialogStyle . generic . backgroundColor
ColumnLayout {
anchors.fill: parent
ScrollView
{
anchors.top: parent . top
anchors.fill: parent
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
ColumnLayout {
id: dialogContent
anchors.top: parent . top
spacing: 10
RowLayout
Layout.preferredWidth: rowWidth
anchors.top: parent . top
anchors.topMargin: 10
anchors.left: parent . left
width: 500
anchors.leftMargin:
{
return ( containerRect . width - 530 ) / 2
}
RowLayout
{
Rectangle
{
id: rowSender
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr ( "Sender" )
Layout.preferredWidth: 150
Label {
anchors.right: parent . right
anchors.verticalCenter: parent . verticalCenter
text: qsTr ( "Sender Account" )
}
ComboBox {
}
function select ( secret )
{
for ( var i in model )
if ( model [ i ] . secret === secret )
{
currentIndex = i ;
break ;
}
}
ComboBox {
function select ( secret )
{
for ( var i in model )
if ( model [ i ] . secret === secret )
{
currentIndex = i ;
break ;
}
}
Layout.preferredWidth: 350
id: senderComboBox
currentIndex: 0
textRole: "name"
editable: false
}
}
id: senderComboBox
Layout.preferredWidth: 350
currentIndex: 0
textRole: "name"
editable: false
RowLayout
{
Rectangle
{
Layout.preferredWidth: 150
Layout.preferredHeight: 80
color: "transparent"
Label
{
anchors.verticalCenter: parent . verticalCenter
anchors.top: parent . top
anchors.right: parent . right
text: qsTr ( "Type of Transaction" )
}
}
RowLayout
Column
{
id: rowIsContract
Layout.fillWidth: true
height: 150
CheckBox {
id: trType
onCheckedChanged:
{
init ( ) ;
Layout.preferredWidth: 350
Layout.preferredHeight: 90
ExclusiveGroup {
id: rbbuttonList
onCurrentChanged: {
if ( current )
{
if ( current . objectName === "trTypeSend" )
{
recipientsAccount . visible = true
contractCreationComboBox . visible = false
modalTransactionDialog . load ( false , false )
}
else if ( current . objectName === "trTypeCreate" )
{
contractCreationComboBox . visible = true
recipientsAccount . visible = false
modalTransactionDialog . load ( true , true )
}
else if ( current . objectName === "trTypeExecute" )
{
recipientsAccount . visible = true
contractCreationComboBox . visible = false
modalTransactionDialog . load ( false , true )
}
}
}
}
function init ( )
{
rowFunction . visible = ! checked ;
rowContract . visible = checked ;
rowRecipient . visible = ! checked ;
paramLabel . visible = checked ;
paramScroll . visible = checked ;
functionComboBox . enabled = ! checked ;
if ( checked )
loadCtorParameters ( contractComboBox . currentValue ( ) ) ;
}
RadioButton {
id: trTypeSend
objectName: "trTypeSend"
exclusiveGroup: rbbuttonList
height: 30
text: qsTr ( "Send ether to account" )
text: qsTr ( "is contract creation" )
checked: true
}
RadioButton {
id: trTypeCreate
objectName: "trTypeCreate"
exclusiveGroup: rbbuttonList
height: 30
text: qsTr ( "Create Contract" )
}
RadioButton {
id: trTypeExecute
objectName: "trTypeExecute"
exclusiveGroup: rbbuttonList
height: 30
text: qsTr ( "Execute Contract" )
}
}
}
RowLayout
RowLayout
{
Rectangle
{
id: rowRecipient
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr ( "Recipient" )
Layout.preferredWidth: 150
Label {
id: labelRecipient
anchors.verticalCenter: parent . verticalCenter
anchors.right: parent . right
text: qsTr ( "Recipient Account " )
}
}
QAddressView
QAddressView
{
id: recipientsAccount
displayInput: false
onIndexChanged:
{
id: recipients
onIndexChanged:
{
rowFunction . visible = current ( ) . type === "contract" ;
paramLabel . visible = current ( ) . type === "contract" ;
paramScroll . visible = current ( ) . type === "contract" ;
if ( ! rowIsContract . checked )
loadFunctions ( contractFromToken ( recipients . currentValue ( ) ) )
}
if ( rbbuttonList . current . objectName === "trTypeExecute" )
loadFunctions ( contractFromToken ( currentValue ( ) ) )
}
}
RowLayout
{
id: rowContract
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr ( "Contract" )
ComboBox {
id: contractCreationComboBox
function currentValue ( ) {
return ( currentIndex >= 0 && currentIndex < contractsModel . count ) ? contractsModel . get ( currentIndex ) . cid : "" ;
}
ComboBox {
id: contractComboBox
function currentValue ( ) {
return ( currentIndex >= 0 && currentIndex < contractsModel . count ) ? contractsModel . get ( currentIndex ) . cid : "" ;
}
Layout.preferredWidth: 350
currentIndex: - 1
textRole: "text"
editable: false
model: ListModel {
id: contractsModel
}
onCurrentIndexChanged: {
loadCtorParameters ( currentValue ( ) ) ;
}
Layout.preferredWidth: 350
currentIndex: - 1
textRole: "text"
editable: false
model: ListModel {
id: contractsModel
}
onCurrentIndexChanged: {
loadCtorParameters ( currentValue ( ) ) ;
}
}
}
RowLayout
RowLayout
{
Rectangle
{
id: rowFunction
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
Layout.preferredWidth: 150
id: functionRect
function hide ( )
{
parent . visible = false
functionRect . visible = false
functionComboBox . visible = false
}
function show ( )
{
parent . visible = true
functionRect . visible = true
functionComboBox . visible = true
}
Label {
anchors.verticalCenter: parent . verticalCenter
anchors.right: parent . right
text: qsTr ( "Function" )
}
ComboBox {
id: functionComboBox
Layout.preferredWidth: 350
currentIndex: - 1
textRole: "text"
editable: false
model: ListModel {
id: functionsModel
}
onCurrentIndexChanged: {
loadParameters ( ) ;
}
}
ComboBox {
id: functionComboBox
Layout.preferredWidth: 350
currentIndex: - 1
textRole: "text"
editable: false
model: ListModel {
id: functionsModel
}
onCurrentIndexChanged: {
loadParameters ( ) ;
}
}
}
CommonSeparator
StructView
{
id: paramScroll
members: paramsModel ;
accounts: senderComboBox . model
context: "parameter"
Layout.fillWidth: true
function updateView ( )
{
Layout.fillWidth: true
paramScroll . visible = paramsModel . length > 0
paramScroll . Layout . preferredHeight = paramsModel . length < 6 ? paramsModel . length * 30 : 205
if ( paramsModel . length === 0 )
{
paramScroll . height = 0
}
}
}
RowLayout
RowLayout
{
Rectangle
{
id: rowValue
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr ( "Value" )
}
Ether {
id: valueField
edit: true
displayFormattedValue: true
Layout.preferredWidth: 150
Label {
id: amountLabel
anchors.verticalCenter: parent . verticalCenter
anchors.right: parent . right
text: qsTr ( "Amount" )
}
}
CommonSeparator
Ether {
Layout.preferredWidth: 350
id: valueField
edit: true
displayFormattedValue: false
displayUnitSelection: true
}
}
Rectangle
{
Layout.preferredHeight: 30
Layout.fillWidth: true
color: "transparent"
Rectangle
{
color: "#cccccc"
height: 1
width: parent . width
anchors.verticalCenter: parent . verticalCenter
}
}
Rectangle
{
height: 20
color: "transparent"
Layout.preferredWidth: 500
Rectangle
{
Layout.fillWidth: true
anchors.horizontalCenter: parent . horizontalCenter
Label {
text: qsTr ( "Transaction fees" )
anchors.horizontalCenter: parent . horizontalCenter
}
}
RowLayout
}
RowLayout
{
Layout.preferredHeight: 45
Rectangle
{
id: rowGas
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
Layout.preferredWidth: 150
Label {
anchors.verticalCenter: parent . verticalCenter
anchors.right: parent . right
text: qsTr ( "Gas" )
}
}
Row
{
Layout.preferredWidth: 350
DefaultTextField
{
property variant gasValue
@ -435,6 +575,55 @@ Dialog {
implicitWidth: 200
enabled: ! gasAutoCheck . checked
id: gasValueEdit ;
Label
{
id: estimatedGas
anchors.top: parent . bottom
text: ""
Connections
{
target: functionComboBox
onCurrentIndexChanged:
{
estimatedGas . displayGas ( contractFromToken ( recipientsAccount . currentValue ( ) ) , functionComboBox . currentText )
}
}
function displayGas ( contractName , functionName )
{
var gasCost = codeModel . gasCostBy ( contractName , functionName ) ;
if ( gasCost && gasCost . length > 0 )
{
var gas = codeModel . txGas + codeModel . callStipend + parseInt ( gasCost [ 0 ] . gas )
estimatedGas . text = qsTr ( "Estimated cost: " ) + gasCost [ 0 ] . gas + " gas"
}
}
function updateView ( )
{
if ( rbbuttonList . current . objectName === "trTypeExecute" )
estimatedGas . displayGas ( contractFromToken ( recipientsAccount . currentValue ( ) ) , functionComboBox . currentText )
else if ( rbbuttonList . current . objectName === "trTypeCreate" )
{
var contractName = contractCreationComboBox . currentValue ( )
estimatedGas . displayGas ( contractName , contractName )
}
else if ( rbbuttonList . current . objectName === "trTypeSend" )
{
var gas = codeModel . txGas + codeModel . callStipend
estimatedGas . text = qsTr ( "Estimated cost: " ) + gas + " gas"
}
}
Connections
{
target: rbbuttonList
onCurrentChanged: {
estimatedGas . updateView ( )
}
}
}
}
CheckBox
@ -444,101 +633,92 @@ Dialog {
text: qsTr ( "Auto" ) ;
}
}
}
CommonSeparator
{
Layout.fillWidth: true
}
RowLayout
RowLayout
{
Layout.preferredWidth: 500
Layout.preferredHeight: 45
Rectangle
{
id: rowGasPrice
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
Layout.preferredWidth: 150
Label {
id: gasPriceLabel
anchors.verticalCenter: parent . verticalCenter
anchors.right: parent . right
text: qsTr ( "Gas Price" )
}
Ether {
id: gasPriceField
edit: true
displayFormattedValue: true
}
}
CommonSeparator
{
Layout.fillWidth: true
}
DefaultLabel {
id: paramLabel
text: qsTr ( "Parameters:" )
Layout.preferredWidth: 75
}
ScrollView
{
id: paramScroll
anchors.top: paramLabel . bottom
anchors.topMargin: 10
Layout.fillWidth: true
Layout.fillHeight: true
StructView
{
id: typeLoader
Layout.preferredWidth: 150
members: paramsModel ;
accounts: senderComboBox . model
context: "parameter"
Label {
id: gasPriceMarket
anchors.top: gasPriceLabel . bottom
Component.onCompleted:
{
NetworkDeployment . gasPrice ( function ( result )
{
gasPriceMarket . text = qsTr ( "Current market: " ) + " " + result + " Wei" ;
} , function ( ) { } ) ;
}
}
}
}
CommonSeparator
{
Layout.fillWidth: true
visible: paramsModel . length > 0
Ether {
Layout.preferredWidth: 350
id: gasPriceField
edit: true
displayFormattedValue: false
displayUnitSelection: true
}
}
}
RowLayout
{
anchors.bottom: parent . bottom
anchors.right: parent . right ;
Button {
RowLayout
{
text: qsTr ( "OK" ) ;
onClicked: {
var invalid = InputValidator . validate ( paramsModel , paramValues ) ;
if ( invalid . length === 0 )
{
close ( ) ;
accepted ( ) ;
Layout.preferredWidth: 500
Row
{
width: parent . width
anchors.right: parent . right
Button {
id: updateBtn
text: qsTr ( "Cancel" ) ;
onClicked: close ( ) ;
}
else
{
errorDialog . text = qsTr ( "Some parameters are invalid:\n" ) ;
for ( var k in invalid )
errorDialog . text += invalid [ k ] . message + "\n" ;
errorDialog . open ( ) ;
Button {
text: qsTr ( "Update" ) ;
onClicked: {
var invalid = InputValidator . validate ( paramsModel , paramValues ) ;
if ( invalid . length === 0 )
{
close ( ) ;
accepted ( ) ;
}
else
{
errorDialog . text = qsTr ( "Some parameters are invalid:\n" ) ;
for ( var k in invalid )
errorDialog . text += invalid [ k ] . message + "\n" ;
errorDialog . open ( ) ;
}
}
}
}
}
Button {
text: qsTr ( "Cancel" ) ;
onClicked: close ( ) ;
MessageDialog {
id: errorDialog
standardButtons: StandardButton . Ok
icon: StandardIcon . Critical
}
}
MessageDialog {
id: errorDialog
standardButtons: StandardButton . Ok
icon: StandardIcon . Critical
RowLayout
{
Layout.preferredHeight: 30
anchors.bottom: parent . bottom
}
}
}
}
}