You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
724 lines
17 KiB
724 lines
17 KiB
import QtQuick 2.2
|
|
import QtQuick.Controls 1.1
|
|
import QtQuick.Layouts 1.1
|
|
import QtQuick.Dialogs 1.2
|
|
import QtQuick.Window 2.0
|
|
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: 580
|
|
height: 500
|
|
visible: false
|
|
title: qsTr("Edit Transaction")
|
|
property int transactionIndex
|
|
property int blockIndex
|
|
property alias gas: gasValueEdit.gasValue;
|
|
property alias gasAuto: gasAutoCheck.checked;
|
|
property alias gasPrice: gasPriceField.value;
|
|
property alias transactionValue: valueField.value;
|
|
property string contractId: contractCreationComboBox.currentValue();
|
|
property alias functionId: functionComboBox.currentText;
|
|
property var paramValues;
|
|
property var paramsModel: [];
|
|
property bool useTransactionDefaultValue: false
|
|
property alias stateAccounts: senderComboBox.model
|
|
property bool saveStatus
|
|
signal accepted;
|
|
property int rowWidth: 500
|
|
StateDialogStyle {
|
|
id: transactionDialogStyle
|
|
}
|
|
|
|
function open(index, blockIdx, item) {
|
|
transactionIndex = index
|
|
blockIndex = blockIdx
|
|
paramScroll.transactionIndex = index
|
|
paramScroll.blockIndex = blockIdx
|
|
saveStatus = item.saveStatus
|
|
gasValueEdit.gasValue = item.gas;
|
|
gasAutoCheck.checked = item.gasAuto ? true : false;
|
|
gasPriceField.value = item.gasPrice;
|
|
valueField.value = item.value;
|
|
var contractId = item.contractId;
|
|
var functionId = item.functionId;
|
|
|
|
paramValues = item.parameters !== undefined ? item.parameters : {};
|
|
if (item.sender)
|
|
senderComboBox.select(item.sender);
|
|
|
|
|
|
trTypeCreate.checked = item.isContractCreation
|
|
trTypeSend.checked = !item.isFunctionCall
|
|
trTypeExecute.checked = item.isFunctionCall && !item.isContractCreation
|
|
|
|
load(item.isContractCreation, item.isFunctionCall, functionId, contractId)
|
|
|
|
estimatedGas.updateView()
|
|
visible = true;
|
|
}
|
|
|
|
function loadCtorParameters(contractId)
|
|
{
|
|
paramsModel = [];
|
|
var contract = codeModel.contracts[contractId];
|
|
if (contract) {
|
|
var params = contract.contract.constructor.parameters;
|
|
for (var p = 0; p < params.length; p++)
|
|
loadParameter(params[p]);
|
|
}
|
|
initTypeLoader();
|
|
}
|
|
|
|
function loadFunctions(contractId)
|
|
{
|
|
functionsModel.clear();
|
|
var contract = codeModel.contracts[contractId];
|
|
if (contract) {
|
|
var functions = codeModel.contracts[contractId].contract.functions;
|
|
for (var f = 0; f < functions.length; f++) {
|
|
if (functions[f].name !== contractId)
|
|
functionsModel.append({ text: functions[f].name });
|
|
}
|
|
}
|
|
}
|
|
|
|
function selectContract(contractName)
|
|
{
|
|
for (var k = 0; k < contractsModel.count; k++)
|
|
{
|
|
if (contractsModel.get(k).cid === contractName)
|
|
{
|
|
contractComboBox.currentIndex = k;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
function selectFunction(functionId)
|
|
{
|
|
var functionIndex = -1;
|
|
for (var f = 0; f < functionsModel.count; f++)
|
|
if (functionsModel.get(f).text === functionId)
|
|
functionIndex = f;
|
|
|
|
if (functionIndex == -1 && functionsModel.count > 0)
|
|
functionIndex = 0; //@todo suggest unused function
|
|
|
|
functionComboBox.currentIndex = functionIndex;
|
|
}
|
|
|
|
function loadParameter(parameter)
|
|
{
|
|
var type = parameter.type;
|
|
var pname = parameter.name;
|
|
paramsModel.push({ name: pname, type: type });
|
|
}
|
|
|
|
function loadParameters() {
|
|
paramsModel = []
|
|
if (functionComboBox.currentIndex >= 0 && functionComboBox.currentIndex < functionsModel.count) {
|
|
var contract = codeModel.contracts[contractFromToken(contractCreationComboBox.currentValue())];
|
|
if (contract) {
|
|
var func = contract.contract.functions[functionComboBox.currentIndex + 1];
|
|
if (func) {
|
|
var parameters = func.parameters;
|
|
for (var p = 0; p < parameters.length; p++)
|
|
loadParameter(parameters[p]);
|
|
}
|
|
}
|
|
}
|
|
initTypeLoader();
|
|
}
|
|
|
|
function initTypeLoader()
|
|
{
|
|
paramScroll.value = {}
|
|
paramScroll.members = []
|
|
paramScroll.value = paramValues;
|
|
paramScroll.members = paramsModel;
|
|
paramScroll.updateView()
|
|
}
|
|
|
|
function acceptAndClose()
|
|
{
|
|
close();
|
|
accepted();
|
|
}
|
|
|
|
function close()
|
|
{
|
|
visible = false;
|
|
}
|
|
|
|
function getItem()
|
|
{
|
|
var item;
|
|
if (!useTransactionDefaultValue)
|
|
{
|
|
item = {
|
|
contractId: transactionDialog.contractId,
|
|
functionId: transactionDialog.functionId,
|
|
gas: transactionDialog.gas,
|
|
gasAuto: transactionDialog.gasAuto,
|
|
gasPrice: transactionDialog.gasPrice,
|
|
value: transactionDialog.transactionValue,
|
|
parameters: {},
|
|
};
|
|
}
|
|
else
|
|
{
|
|
item = TransactionHelper.defaultTransaction();
|
|
item.contractId = transactionDialog.contractId;
|
|
item.functionId = transactionDialog.functionId;
|
|
}
|
|
|
|
item.isContractCreation = trTypeCreate.checked;
|
|
if (item.isContractCreation)
|
|
item.functionId = item.contractId;
|
|
item.isFunctionCall = trTypeExecute.checked
|
|
|
|
if (!item.isContractCreation)
|
|
{
|
|
item.contractId = recipientsAccount.currentValue();
|
|
item.label = contractFromToken(item.contractId) + "." + item.functionId + "()";
|
|
if (recipientsAccount.current().type === "address")
|
|
{
|
|
item.functionId = "";
|
|
item.isFunctionCall = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
item.isFunctionCall = true
|
|
item.functionId = item.contractId;
|
|
item.label = item.contractId + "." + item.contractId + "()";
|
|
}
|
|
item.saveStatus = saveStatus
|
|
item.sender = senderComboBox.model[senderComboBox.currentIndex].secret;
|
|
item.parameters = paramValues;
|
|
return item;
|
|
}
|
|
|
|
function contractFromToken(token)
|
|
{
|
|
if (token.indexOf('<') === 0)
|
|
return token.replace("<", "").replace(">", "").split(" - ")[0];
|
|
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
|
|
{
|
|
//contract creation
|
|
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; //@todo suggest unused contract
|
|
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
|
|
anchors.fill: parent
|
|
ScrollView
|
|
{
|
|
anchors.top: parent.top
|
|
anchors.fill: parent
|
|
ColumnLayout {
|
|
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
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
Layout.preferredWidth: 350
|
|
id: senderComboBox
|
|
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")
|
|
}
|
|
}
|
|
|
|
Column
|
|
{
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RadioButton {
|
|
id: trTypeSend
|
|
objectName: "trTypeSend"
|
|
exclusiveGroup: rbbuttonList
|
|
height: 30
|
|
text: qsTr("Send ether to account")
|
|
|
|
}
|
|
|
|
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
|
|
{
|
|
Rectangle
|
|
{
|
|
Layout.preferredWidth: 150
|
|
Label {
|
|
id: labelRecipient
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.right: parent.right
|
|
text: qsTr("Recipient Account")
|
|
}
|
|
}
|
|
|
|
QAddressView
|
|
{
|
|
id: recipientsAccount
|
|
displayInput: false
|
|
onIndexChanged:
|
|
{
|
|
if (rbbuttonList.current.objectName === "trTypeExecute")
|
|
loadFunctions(contractFromToken(currentValue()))
|
|
}
|
|
}
|
|
|
|
ComboBox {
|
|
id: contractCreationComboBox
|
|
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());
|
|
}
|
|
}
|
|
}
|
|
|
|
RowLayout
|
|
{
|
|
Rectangle
|
|
{
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
|
|
StructView
|
|
{
|
|
id: paramScroll
|
|
members: paramsModel;
|
|
accounts: senderComboBox.model
|
|
context: "parameter"
|
|
Layout.fillWidth: true
|
|
function updateView()
|
|
{
|
|
paramScroll.visible = paramsModel.length > 0
|
|
paramScroll.Layout.preferredHeight = paramsModel.length < 6 ? paramsModel.length * 30 : 205
|
|
if (paramsModel.length === 0)
|
|
{
|
|
paramScroll.height = 0
|
|
}
|
|
}
|
|
}
|
|
|
|
RowLayout
|
|
{
|
|
Rectangle
|
|
{
|
|
Layout.preferredWidth: 150
|
|
Label {
|
|
id: amountLabel
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.right: parent.right
|
|
text: qsTr("Amount")
|
|
}
|
|
}
|
|
|
|
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
|
|
{
|
|
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
Label {
|
|
text: qsTr("Transaction fees")
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
RowLayout
|
|
{
|
|
Layout.preferredHeight: 45
|
|
Rectangle
|
|
{
|
|
Layout.preferredWidth: 150
|
|
Label {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.right: parent.right
|
|
text: qsTr("Gas")
|
|
}
|
|
}
|
|
|
|
Row
|
|
{
|
|
Layout.preferredWidth: 350
|
|
DefaultTextField
|
|
{
|
|
property variant gasValue
|
|
onGasValueChanged: text = gasValue.value();
|
|
onTextChanged: gasValue.setValue(text);
|
|
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
|
|
{
|
|
id: gasAutoCheck
|
|
checked: true
|
|
text: qsTr("Auto");
|
|
}
|
|
}
|
|
}
|
|
|
|
RowLayout
|
|
{
|
|
Layout.preferredWidth: 500
|
|
Layout.preferredHeight: 45
|
|
Rectangle
|
|
{
|
|
Layout.preferredWidth: 150
|
|
Label {
|
|
id: gasPriceLabel
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.right: parent.right
|
|
text: qsTr("Gas Price")
|
|
|
|
Label {
|
|
id: gasPriceMarket
|
|
anchors.top: gasPriceLabel.bottom
|
|
Component.onCompleted:
|
|
{
|
|
NetworkDeployment.gasPrice(function(result)
|
|
{
|
|
gasPriceMarket.text = qsTr("Current market: ") + " " + result + " Wei";
|
|
}, function (){});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Ether {
|
|
Layout.preferredWidth: 350
|
|
id: gasPriceField
|
|
edit: true
|
|
displayFormattedValue: false
|
|
displayUnitSelection: true
|
|
}
|
|
}
|
|
|
|
|
|
RowLayout
|
|
{
|
|
|
|
Layout.preferredWidth: 500
|
|
Row
|
|
{
|
|
width: parent.width
|
|
anchors.right: parent.right
|
|
Button {
|
|
id: updateBtn
|
|
text: qsTr("Cancel");
|
|
onClicked: close();
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
MessageDialog {
|
|
id: errorDialog
|
|
standardButtons: StandardButton.Ok
|
|
icon: StandardIcon.Critical
|
|
}
|
|
}
|
|
|
|
RowLayout
|
|
{
|
|
Layout.preferredHeight: 30
|
|
anchors.bottom: parent.bottom
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|