Browse Source

allow more than one contract

cl-refactor
arkpar 10 years ago
parent
commit
7af5ac1252
  1. 2
      libsolidity/Compiler.h
  2. 2
      mix/CMakeLists.txt
  3. 67
      mix/ClientModel.cpp
  4. 21
      mix/ClientModel.h
  5. 213
      mix/CodeModel.cpp
  6. 76
      mix/CodeModel.h
  7. 2
      mix/QContractDefinition.cpp
  8. 2
      mix/QContractDefinition.h
  9. 11
      mix/StatusPane.cpp
  10. 4
      mix/StatusPane.h
  11. 2
      mix/qml/CodeEditorView.qml
  12. 2
      mix/qml/Debugger.qml
  13. 2
      mix/qml/MainContent.qml
  14. 21
      mix/qml/ProjectList.qml
  15. 2
      mix/qml/ProjectModel.qml
  16. 18
      mix/qml/StateListModel.qml
  17. 14
      mix/qml/StatusPane.qml
  18. 107
      mix/qml/TransactionDialog.qml
  19. 12
      mix/qml/WebPreview.qml
  20. 16
      mix/qml/html/WebContainer.html
  21. 77
      mix/qml/js/ProjectModel.js
  22. 1
      mix/qml/js/TransactionHelper.js
  23. 2
      mix/qml/main.qml

2
libsolidity/Compiler.h

@ -20,6 +20,8 @@
* Solidity AST to EVM bytecode compiler.
*/
#pragma once
#include <ostream>
#include <functional>
#include <libsolidity/ASTVisitor.h>

2
mix/CMakeLists.txt

@ -71,4 +71,4 @@ eth_install_executable(${EXECUTABLE}
#add qml asnd stdc files to project tree in Qt creator
file(GLOB_RECURSE QMLFILES "qml/*.*")
file(GLOB_RECURSE SOLFILES "stdc/*.*")
add_custom_target(dummy SOURCES ${QMLFILES} ${SOLFILES})
add_custom_target(mix_qml SOURCES ${QMLFILES} ${SOLFILES})

67
mix/ClientModel.cpp

@ -65,7 +65,7 @@ private:
ClientModel::ClientModel(AppContext* _context):
m_context(_context), m_running(false), m_rpcConnector(new RpcConnector()), m_contractAddress(Address())
m_context(_context), m_running(false), m_rpcConnector(new RpcConnector())
{
qRegisterMetaType<QBigInt*>("QBigInt*");
qRegisterMetaType<QIntType*>("QIntType*");
@ -136,9 +136,12 @@ void ClientModel::mine()
});
}
QString ClientModel::contractAddress() const
QVariantMap ClientModel::contractAddresses() const
{
return QString::fromStdString(dev::toJS(m_contractAddress));
QVariantMap res;
for (auto const& c: m_contractAddresses)
res.insert(c.first, QString::fromStdString(dev::toJS(c.second)));
return res;
}
void ClientModel::debugDeployment()
@ -155,8 +158,8 @@ void ClientModel::setupState(QVariantMap _state)
for (auto const& t: transactions)
{
QVariantMap transaction = t.toMap();
QString contractId = transaction.value("contractId").toString();
QString functionId = transaction.value("functionId").toString();
u256 gas = boost::get<u256>(qvariant_cast<QBigInt*>(transaction.value("gas"))->internalValue());
u256 value = (qvariant_cast<QEther*>(transaction.value("value")))->toU256Wei();
u256 gasPrice = (qvariant_cast<QEther*>(transaction.value("gasPrice")))->toU256Wei();
@ -164,7 +167,9 @@ void ClientModel::setupState(QVariantMap _state)
bool isStdContract = (transaction.value("stdContract").toBool());
if (isStdContract)
{
TransactionSettings transactionSettings(functionId, transaction.value("url").toString());
if (contractId.isEmpty()) //TODO: This is to support old project files, remove later
contractId = functionId;
TransactionSettings transactionSettings(contractId, transaction.value("url").toString());
transactionSettings.gasPrice = 10000000000000;
transactionSettings.gas = 125000;
transactionSettings.value = 0;
@ -172,8 +177,10 @@ void ClientModel::setupState(QVariantMap _state)
}
else
{
if (contractId.isEmpty() && m_context->codeModel()->hasContract()) //TODO: This is to support old project files, remove later
contractId = m_context->codeModel()->contracts().keys()[0];
QVariantList qParams = transaction.value("qType").toList();
TransactionSettings transactionSettings(functionId, value, gas, gasPrice);
TransactionSettings transactionSettings(contractId, functionId, value, gas, gasPrice);
for (QVariant const& variant: qParams)
{
@ -181,7 +188,7 @@ void ClientModel::setupState(QVariantMap _state)
transactionSettings.parameterValues.push_back(param);
}
if (transaction.value("executeConstructor").toBool())
if (contractId == functionId || functionId == "Constructor")
transactionSettings.functionId.clear();
transactionSequence.push_back(transactionSettings);
@ -194,8 +201,6 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
{
if (m_running)
BOOST_THROW_EXCEPTION(ExecutionStateException());
CompilationResult* compilerRes = m_context->codeModel()->code();
std::shared_ptr<QContractDefinition> contractDef = compilerRes->sharedContract();
m_running = true;
emit runStarted();
@ -206,7 +211,6 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
{
try
{
bytes contractCode = compilerRes->bytes();
m_client->resetState(_balance);
onStateReset();
for (TransactionSettings const& transaction: _sequence)
@ -216,14 +220,17 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
if (!transaction.stdContractUrl.isEmpty())
{
//std contract
dev::bytes const& stdContractCode = m_context->codeModel()->getStdContractCode(transaction.functionId, transaction.stdContractUrl);
dev::bytes const& stdContractCode = m_context->codeModel()->getStdContractCode(transaction.contractId, transaction.stdContractUrl);
Address address = deployContract(stdContractCode, transaction);
m_stdContractAddresses[transaction.functionId] = address;
m_stdContractNames[address] = transaction.functionId;
m_stdContractAddresses[transaction.contractId] = address;
m_stdContractNames[address] = transaction.contractId;
}
else
{
//encode data
CompiledContract const& compilerRes = m_context->codeModel()->contract(transaction.contractId);
bytes contractCode = compilerRes.bytes();
std::shared_ptr<QContractDefinition> contractDef = compilerRes.sharedContract();
f = nullptr;
if (transaction.functionId.isEmpty())
f = contractDef->constructor();
@ -240,24 +247,31 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
encoder.encode(f);
for (int p = 0; p < transaction.parameterValues.size(); p++)
{
if (f->parametersList().at(p)->type() != transaction.parameterValues.at(p)->declaration()->type())
BOOST_THROW_EXCEPTION(ParameterChangedException() << FunctionName(f->parametersList().at(p)->type().toStdString()));
if (f->parametersList().size() <= p || f->parametersList().at(p)->type() != transaction.parameterValues.at(p)->declaration()->type())
BOOST_THROW_EXCEPTION(ParameterChangedException() << FunctionName(transaction.functionId.toStdString()));
encoder.push(transaction.parameterValues.at(p)->encodeValue());
}
if (transaction.functionId.isEmpty())
if (transaction.functionId.isEmpty() || transaction.functionId == transaction.contractId)
{
bytes param = encoder.encodedData();
contractCode.insert(contractCode.end(), param.begin(), param.end());
Address newAddress = deployContract(contractCode, transaction);
if (newAddress != m_contractAddress)
auto contractAddressIter = m_contractAddresses.find(transaction.contractId);
if (contractAddressIter == m_contractAddresses.end() || newAddress != contractAddressIter->second)
{
m_contractAddress = newAddress;
contractAddressChanged();
m_contractAddresses[transaction.contractId] = newAddress;
m_contractNames[newAddress] = transaction.contractId;
contractAddressesChanged();
}
}
else
callContract(m_contractAddress, encoder.encodedData(), transaction);
{
auto contractAddressIter = m_contractAddresses.find(transaction.contractId);
if (contractAddressIter == m_contractAddresses.end())
BOOST_THROW_EXCEPTION(dev::Exception() << dev::errinfo_comment("Contract not deployed: " + transaction.contractId.toStdString()));
callContract(contractAddressIter->second, encoder.encodedData(), transaction);
}
}
onNewTransaction();
}
@ -338,7 +352,8 @@ void ClientModel::callContract(Address const& _contract, bytes const& _data, Tra
void ClientModel::onStateReset()
{
m_contractAddress = dev::Address();
m_contractAddresses.clear();
m_contractNames.clear();
m_stdContractAddresses.clear();
m_stdContractNames.clear();
emit stateCleared();
@ -389,14 +404,16 @@ void ClientModel::onNewTransaction()
if (creation)
returned = QString::fromStdString(toJS(tr.contractAddress));
if (m_contractAddress != 0 && (tr.address == m_contractAddress || tr.contractAddress == m_contractAddress))
Address contractAddress = tr.address != 0 ? tr.address : tr.contractAddress;
auto contractAddressIter = m_contractNames.find(contractAddress);
if (contractAddressIter != m_contractNames.end())
{
auto compilerRes = m_context->codeModel()->code();
QContractDefinition* def = compilerRes->contract();
CompiledContract const& compilerRes = m_context->codeModel()->contract(contractAddressIter->second);
const QContractDefinition* def = compilerRes.contract();
contract = def->name();
if (abi)
{
QFunctionDefinition* funcDef = def->getFunction(functionHash);
QFunctionDefinition const* funcDef = def->getFunction(functionHash);
if (funcDef)
{
function = funcDef->name();

21
mix/ClientModel.h

@ -46,13 +46,13 @@ class QVariableDefinition;
struct TransactionSettings
{
TransactionSettings() {}
TransactionSettings(QString const& _functionId, u256 _value, u256 _gas, u256 _gasPrice):
functionId(_functionId), value(_value), gas(_gas), gasPrice(_gasPrice) {}
TransactionSettings(u256 _value, u256 _gas, u256 _gasPrice):
value(_value), gas(_gas), gasPrice(_gasPrice) {}
TransactionSettings(QString const& _contractId, QString const& _functionId, u256 _value, u256 _gas, u256 _gasPrice):
contractId(_contractId), functionId(_functionId), value(_value), gas(_gas), gasPrice(_gasPrice) {}
TransactionSettings(QString const& _stdContractName, QString const& _stdContractUrl):
functionId(_stdContractName), stdContractUrl(_stdContractUrl) {}
contractId(_stdContractName), stdContractUrl(_stdContractUrl) {}
/// Contract name
QString contractId;
/// Contract function name
QString functionId;
/// Transaction value
@ -121,8 +121,8 @@ public:
Q_PROPERTY(bool running MEMBER m_running NOTIFY runStateChanged)
/// @returns true if currently mining
Q_PROPERTY(bool mining MEMBER m_mining NOTIFY miningStateChanged)
/// @returns address of the last executed contract
Q_PROPERTY(QString contractAddress READ contractAddress NOTIFY contractAddressChanged)
/// @returns deployed contracts addresses
Q_PROPERTY(QVariantMap contractAddresses READ contractAddresses NOTIFY contractAddressesChanged)
/// ethereum.js RPC request entry point
/// @param _message RPC request in Json format
/// @returns RPC response in Json format
@ -161,7 +161,7 @@ signals:
/// @param _message Error message
void runFailed(QString const& _message);
/// Contract address changed
void contractAddressChanged();
void contractAddressesChanged();
/// Execution state changed
void newBlock();
/// Execution state changed
@ -177,7 +177,7 @@ signals:
void stateCleared();
private:
QString contractAddress() const;
QVariantMap contractAddresses() const;
void executeSequence(std::vector<TransactionSettings> const& _sequence, u256 _balance);
dev::Address deployContract(bytes const& _code, TransactionSettings const& _tr = TransactionSettings());
void callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr);
@ -191,7 +191,8 @@ private:
std::unique_ptr<MixClient> m_client;
std::unique_ptr<RpcConnector> m_rpcConnector;
std::unique_ptr<Web3Server> m_web3Server;
Address m_contractAddress;
std::map<QString, Address> m_contractAddresses;
std::map<Address, QString> m_contractNames;
std::map<QString, Address> m_stdContractAddresses;
std::map<Address, QString> m_stdContractNames;
};

213
mix/CodeModel.cpp

@ -21,6 +21,7 @@
*/
#include <sstream>
#include <memory>
#include <QDebug>
#include <QApplication>
#include <QtQml>
@ -38,51 +39,31 @@
using namespace dev::mix;
void BackgroundWorker::queueCodeChange(int _jobId, QString const& _content)
const std::set<std::string> c_predefinedContracts =
{ "Config", "Coin", "CoinReg", "coin", "service", "owned", "mortal", "NameReg", "named", "std", "configUser" };
void BackgroundWorker::queueCodeChange(int _jobId)
{
m_model->runCompilationJob(_jobId, _content);
m_model->runCompilationJob(_jobId);
}
CompilationResult::CompilationResult():
QObject(nullptr),
m_successful(false),
m_codeHash(qHash(QString())),
m_contract(new QContractDefinition()),
m_contractInterface("[]"),
m_codeHighlighter(new CodeHighlighter())
{}
CompilationResult::CompilationResult(const dev::solidity::CompilerStack& _compiler):
CompiledContract::CompiledContract(const dev::solidity::CompilerStack& _compiler, QString const& _contractName, QString const& _source):
QObject(nullptr),
m_successful(true),
m_codeHash(qHash(QString()))
m_sourceHash(qHash(_source))
{
if (!_compiler.getContractNames().empty())
{
auto const& contractDefinition = _compiler.getContractDefinition(std::string());
m_contract.reset(new QContractDefinition(&contractDefinition));
m_bytes = _compiler.getBytecode();
dev::solidity::InterfaceHandler interfaceHandler;
m_contractInterface = QString::fromStdString(*interfaceHandler.getABIInterface(contractDefinition));
if (m_contractInterface.isEmpty())
m_contractInterface = "[]";
}
else
m_contract.reset(new QContractDefinition());
auto const& contractDefinition = _compiler.getContractDefinition(_contractName.toStdString());
m_contract.reset(new QContractDefinition(&contractDefinition));
QQmlEngine::setObjectOwnership(m_contract.get(), QQmlEngine::CppOwnership);
m_bytes = _compiler.getBytecode(_contractName.toStdString());
dev::solidity::InterfaceHandler interfaceHandler;
m_contractInterface = QString::fromStdString(*interfaceHandler.getABIInterface(contractDefinition));
if (m_contractInterface.isEmpty())
m_contractInterface = "[]";
if (contractDefinition.getLocation().sourceName.get())
m_documentId = QString::fromStdString(*contractDefinition.getLocation().sourceName);
}
CompilationResult::CompilationResult(CompilationResult const& _prev, QString const& _compilerMessage):
QObject(nullptr),
m_successful(false),
m_codeHash(qHash(QString())),
m_contract(_prev.m_contract),
m_compilerMessage(_compilerMessage),
m_bytes(_prev.m_bytes),
m_contractInterface(_prev.m_contractInterface),
m_codeHighlighter(_prev.m_codeHighlighter)
{}
QString CompilationResult::codeHex() const
QString CompiledContract::codeHex() const
{
return QString::fromStdString(toJS(m_bytes));
}
@ -90,27 +71,26 @@ QString CompilationResult::codeHex() const
CodeModel::CodeModel(QObject* _parent):
QObject(_parent),
m_compiling(false),
m_result(new CompilationResult()),
m_codeHighlighterSettings(new CodeHighlighterSettings()),
m_backgroundWorker(this),
m_backgroundJobId(0)
{
m_backgroundThread.start();
m_backgroundWorker.moveToThread(&m_backgroundThread);
connect(this, &CodeModel::scheduleCompilationJob, &m_backgroundWorker, &BackgroundWorker::queueCodeChange, Qt::QueuedConnection);
connect(this, &CodeModel::compilationCompleteInternal, this, &CodeModel::onCompilationComplete, Qt::QueuedConnection);
qRegisterMetaType<CompilationResult*>("CompilationResult*");
qRegisterMetaType<CompiledContract*>("CompiledContract*");
qRegisterMetaType<QContractDefinition*>("QContractDefinition*");
qRegisterMetaType<QFunctionDefinition*>("QFunctionDefinition*");
qRegisterMetaType<QVariableDeclaration*>("QVariableDeclaration*");
qmlRegisterType<QFunctionDefinition>("org.ethereum.qml", 1, 0, "QFunctionDefinition");
qmlRegisterType<QVariableDeclaration>("org.ethereum.qml", 1, 0, "QVariableDeclaration");
m_backgroundThread.start();
}
CodeModel::~CodeModel()
{
stop();
disconnect(this);
releaseContracts();
}
void CodeModel::stop()
@ -120,80 +100,133 @@ void CodeModel::stop()
m_backgroundThread.wait();
}
void CodeModel::registerCodeChange(QString const& _code)
void CodeModel::reset(QVariantMap const& _documents)
{
///@todo: cancel bg job
Guard l(x_contractMap);
releaseContracts();
Guard pl(x_pendingContracts);
m_pendingContracts.clear();
for (QVariantMap::const_iterator d = _documents.cbegin(); d != _documents.cend(); d++)
m_pendingContracts[d.key()] = d.value().toString();
// launch the background thread
uint hash = qHash(_code);
if (m_result->m_codeHash == hash)
return;
m_backgroundJobId++;
m_compiling = true;
emit stateChanged();
emit scheduleCompilationJob(m_backgroundJobId, _code);
emit scheduleCompilationJob(++m_backgroundJobId);
}
void CodeModel::runCompilationJob(int _jobId, QString const& _code)
void CodeModel::registerCodeChange(QString const& _documentId, QString const& _code)
{
if (_jobId != m_backgroundJobId)
return; //obsolete job
{
Guard l(x_contractMap);
CompiledContract* contract = m_contractMap.value(_documentId);
if (contract != nullptr && contract->m_sourceHash == qHash(_code))
return;
solidity::CompilerStack cs(true);
std::unique_ptr<CompilationResult> result;
Guard pl(x_pendingContracts);
m_pendingContracts[_documentId] = _code;
}
// launch the background thread
m_compiling = true;
emit stateChanged();
emit scheduleCompilationJob(++m_backgroundJobId);
}
QVariantMap CodeModel::contracts() const
{
QVariantMap result;
Guard l(x_contractMap);
for (ContractMap::const_iterator c = m_contractMap.cbegin(); c != m_contractMap.cend(); c++)
result.insert(c.key(), QVariant::fromValue(c.value()));
return result;
}
std::string source = _code.toStdString();
// run syntax highlighting first
// @todo combine this with compilation step
auto codeHighlighter = std::make_shared<CodeHighlighter>();
codeHighlighter->processSource(source);
CompiledContract* CodeModel::contractByDocumentId(QString _documentId) const
{
Guard l(x_contractMap);
for (ContractMap::const_iterator c = m_contractMap.cbegin(); c != m_contractMap.cend(); c++)
if (c.value()->m_documentId == _documentId)
return c.value();
return nullptr;
}
CompiledContract const& CodeModel::contract(QString _name) const
{
Guard l(x_contractMap);
CompiledContract* res = m_contractMap.value(_name);
if (res == nullptr)
BOOST_THROW_EXCEPTION(dev::Exception() << dev::errinfo_comment("Contract not found: " + _name.toStdString()));
return *res;
}
cs.addSource("configUser", R"(contract configUser{function configAddr()constant returns(address a){ return 0xf025d81196b72fba60a1d4dddad12eeb8360d828;}})");
void CodeModel::releaseContracts()
{
for (ContractMap::iterator c = m_contractMap.begin(); c != m_contractMap.end(); c++)
c.value()->deleteLater();
m_contractMap.clear();
}
void CodeModel::runCompilationJob(int _jobId)
{
if (_jobId != m_backgroundJobId)
return; //obsolete job
// run compilation
ContractMap result;
solidity::CompilerStack cs(true);
try
{
cs.addSource("", source);
cs.addSource("configUser", R"(contract configUser{function configAddr()constant returns(address a){ return 0xf025d81196b72fba60a1d4dddad12eeb8360d828;}})");
{
Guard l(x_pendingContracts);
for (auto const& c: m_pendingContracts)
cs.addSource(c.first.toStdString(), c.second.toStdString());
}
cs.compile(false);
codeHighlighter->processAST(cs.getAST());
result.reset(new CompilationResult(cs));
qDebug() << QString(QApplication::tr("compilation succeeded"));
{
Guard pl(x_pendingContracts);
Guard l(x_contractMap);
for (std::string n: cs.getContractNames())
{
if (c_predefinedContracts.count(n) != 0)
continue;
QString name = QString::fromStdString(n);
auto sourceIter = m_pendingContracts.find(name);
QString source = sourceIter != m_pendingContracts.end() ? sourceIter->second : QString();
CompiledContract* contract = new CompiledContract(cs, name, source);
QQmlEngine::setObjectOwnership(contract, QQmlEngine::CppOwnership);
result[name] = contract;
CompiledContract* prevContract = m_contractMap.value(name);
if (prevContract != nullptr && prevContract->contractInterface() != result[name]->contractInterface())
emit contractInterfaceChanged(name);
}
releaseContracts();
m_contractMap.swap(result);
emit codeChanged();
emit compilationComplete();
}
}
catch (dev::Exception const& _exception)
{
std::ostringstream error;
solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", cs);
result.reset(new CompilationResult(*m_result, QString::fromStdString(error.str())));
codeHighlighter->processError(_exception);
qDebug() << QString(QApplication::tr("compilation failed:") + " " + result->compilerMessage());
solidity::Location const* location = boost::get_error_info<solidity::errinfo_sourceLocation>(_exception);
QString message = QString::fromStdString(error.str());
CompiledContract* contract = nullptr;
if (location && location->sourceName.get() && (contract = contractByDocumentId(QString::fromStdString(*location->sourceName))))
message = message.replace(QString::fromStdString(*location->sourceName), contract->contract()->name()); //substitute the location to match our contract names
compilationError(message);
}
result->m_codeHighlighter = codeHighlighter;
result->m_codeHash = qHash(_code);
emit compilationCompleteInternal(result.release());
}
void CodeModel::onCompilationComplete(CompilationResult* _newResult)
{
m_compiling = false;
bool contractChanged = m_result->contractInterface() != _newResult->contractInterface();
m_result.reset(_newResult);
emit compilationComplete();
emit stateChanged();
if (m_result->successful())
{
emit codeChanged();
if (contractChanged)
emit contractInterfaceChanged();
}
}
bool CodeModel::hasContract() const
{
return m_result->successful();
}
void CodeModel::updateFormatting(QTextDocument* _document)
{
m_result->codeHighlighter()->updateFormatting(_document, *m_codeHighlighterSettings);
Guard l(x_contractMap);
return m_contractMap.size() != 0;
}
dev::bytes const& CodeModel::getStdContractCode(const QString& _contractName, const QString& _url)

76
mix/CodeModel.h

@ -27,7 +27,9 @@
#include <map>
#include <QObject>
#include <QThread>
#include <QHash>
#include <libdevcore/Common.h>
#include <libdevcore/Guards.h>
class QTextDocument;
@ -56,59 +58,50 @@ public:
BackgroundWorker(CodeModel* _model): QObject(), m_model(_model) {}
public slots:
void queueCodeChange(int _jobId, QString const& _content);
void queueCodeChange(int _jobId);
private:
CodeModel* m_model;
};
///Compilation result model. Contains all the compiled contract data required by UI
class CompilationResult: public QObject
class CompiledContract: public QObject
{
Q_OBJECT
Q_PROPERTY(QContractDefinition* contract READ contract)
Q_PROPERTY(QString compilerMessage READ compilerMessage CONSTANT)
Q_PROPERTY(bool successful READ successful CONSTANT)
Q_PROPERTY(QString contractInterface READ contractInterface CONSTANT)
Q_PROPERTY(QString codeHex READ codeHex CONSTANT)
Q_PROPERTY(QString documentId MEMBER m_documentId CONSTANT)
public:
/// Empty compilation result constructor
CompilationResult();
/// Successful compilation result constructor
CompilationResult(solidity::CompilerStack const& _compiler);
/// Failed compilation result constructor
CompilationResult(CompilationResult const& _prev, QString const& _compilerMessage);
CompiledContract(solidity::CompilerStack const& _compiler, QString const& _contractName, QString const& _source);
/// @returns contract definition for QML property
QContractDefinition* contract() { return m_contract.get(); }
QContractDefinition* contract() const { return m_contract.get(); }
/// @returns contract definition
std::shared_ptr<QContractDefinition> sharedContract() { return m_contract; }
/// Indicates if the compilation was successful
bool successful() const { return m_successful; }
/// @returns compiler error message in case of unsuccessful compilation
QString compilerMessage() const { return m_compilerMessage; }
std::shared_ptr<QContractDefinition> sharedContract() const { return m_contract; }
/// @returns contract bytecode
dev::bytes const& bytes() const { return m_bytes; }
/// @returns contract bytecode as hex string
QString codeHex() const;
/// @returns contract definition in JSON format
QString contractInterface() const { return m_contractInterface; }
/// Get code highlighter
std::shared_ptr<CodeHighlighter> codeHighlighter() { return m_codeHighlighter; }
private:
bool m_successful;
uint m_codeHash;
uint m_sourceHash;
std::shared_ptr<QContractDefinition> m_contract;
QString m_compilerMessage; ///< @todo: use some structure here
dev::bytes m_bytes;
QString m_contractInterface;
std::shared_ptr<CodeHighlighter> m_codeHighlighter;
QString m_documentId;
friend class CodeModel;
};
/// Background code compiler
using ContractMap = QHash<QString, CompiledContract*>;
/// Code compilation model. Compiles contracts in background an provides compiled contract data
class CodeModel: public QObject
{
Q_OBJECT
@ -117,56 +110,59 @@ public:
CodeModel(QObject* _parent);
~CodeModel();
/// @returns latest compilation result
CompilationResult* code() { return m_result.get(); }
/// @returns latest compilation resul
CompilationResult const* code() const { return m_result.get(); }
Q_PROPERTY(CompilationResult* code READ code NOTIFY codeChanged)
Q_PROPERTY(QVariantMap contracts READ contracts NOTIFY codeChanged)
Q_PROPERTY(bool compiling READ isCompiling NOTIFY stateChanged)
Q_PROPERTY(bool hasContract READ hasContract NOTIFY codeChanged)
/// @returns latest compilation results for contracts
QVariantMap contracts() const;
/// @returns compilation status
bool isCompiling() const { return m_compiling; }
/// @returns true if contract has at least one function
/// @returns true there is a contract which has at least one function
bool hasContract() const;
/// Apply text document formatting. @todo Move this to editor module
void updateFormatting(QTextDocument* _document);
/// Get contract code by url. Contract is compiled on first access and cached
dev::bytes const& getStdContractCode(QString const& _contractName, QString const& _url);
/// Get contract by name
CompiledContract const& contract(QString _name) const;
/// Find a contract by document id
/// @returns CompiledContract object or null if not found
Q_INVOKABLE CompiledContract* contractByDocumentId(QString _documentId) const;
signals:
/// Emited on compilation state change
void stateChanged();
/// Emitted on compilation complete
void compilationComplete();
/// Emitted on compilation error
void compilationError(QString _error);
/// Internal signal used to transfer compilation job to background thread
void scheduleCompilationJob(int _jobId, QString const& _content);
void scheduleCompilationJob(int _jobId);
/// Emitted if there are any changes in the code model
void codeChanged();
/// Emitted if there are any changes in the contract interface
void contractInterfaceChanged();
/// Emitted on compilation complete. Internal
void compilationCompleteInternal(CompilationResult* _newResult);
private slots:
void onCompilationComplete(CompilationResult* _newResult);
void contractInterfaceChanged(QString _documentId);
public slots:
/// Update code model on source code change
void registerCodeChange(QString const& _code);
void registerCodeChange(QString const& _documentId, QString const& _code);
/// Reset code model for a new project
void reset(QVariantMap const& _documents);
private:
void runCompilationJob(int _jobId, QString const& _content);
void runCompilationJob(int _jobId);
void stop();
void releaseContracts();
std::atomic<bool> m_compiling;
std::unique_ptr<CompilationResult> m_result;
mutable dev::Mutex x_contractMap;
ContractMap m_contractMap;
std::unique_ptr<CodeHighlighterSettings> m_codeHighlighterSettings;
QThread m_backgroundThread;
BackgroundWorker m_backgroundWorker;
int m_backgroundJobId = 0; //protects from starting obsolete compilation job
std::map<QString, dev::bytes> m_compiledContracts; //by name
dev::Mutex x_pendingContracts;
std::map<QString, QString> m_pendingContracts; //name to source
friend class BackgroundWorker;
};

2
mix/QContractDefinition.cpp

@ -42,7 +42,7 @@ QContractDefinition::QContractDefinition(dev::solidity::ContractDefinition const
m_functions.append(new QFunctionDefinition(it.second));}
QFunctionDefinition* QContractDefinition::getFunction(dev::FixedHash<4> _hash)
QFunctionDefinition const* QContractDefinition::getFunction(dev::FixedHash<4> _hash) const
{
for (auto const& f: m_functions)
if (f->hash() == _hash)

2
mix/QContractDefinition.h

@ -47,7 +47,7 @@ public:
QFunctionDefinition* constructor() const { return m_constructor; }
QList<QFunctionDefinition*> const& functionsList() const { return m_functions; }
/// Find function by hash, returns nullptr if not found
QFunctionDefinition* getFunction(dev::FixedHash<4> _hash);
QFunctionDefinition const* getFunction(dev::FixedHash<4> _hash) const;
private:
QList<QFunctionDefinition*> m_functions;
QFunctionDefinition* m_constructor;

11
mix/StatusPane.cpp

@ -36,7 +36,6 @@ using namespace dev::mix;
StatusPane::StatusPane(AppContext* _context): Extension(_context, ExtensionDisplayBehavior::HeaderView)
{
connect(_context->codeModel(), &CodeModel::compilationComplete, this, &StatusPane::update);
_context->appEngine()->rootContext()->setContextProperty("statusPane", this);
}
@ -54,13 +53,3 @@ void StatusPane::start() const
{
}
CompilationResult* StatusPane::result() const
{
return m_ctx->codeModel()->code();
}
void StatusPane::update()
{
QObject* ctrl = m_view->findChild<QObject*>("statusPane", Qt::FindChildrenRecursively);
QMetaObject::invokeMethod(ctrl, "updateStatus");
}

4
mix/StatusPane.h

@ -20,7 +20,6 @@
#pragma once
#include "Extension.h"
#include "CodeModel.h"
namespace dev
{
@ -33,7 +32,6 @@ namespace mix
class StatusPane: public Extension
{
Q_OBJECT
Q_PROPERTY(CompilationResult* result READ result CONSTANT)
public:
StatusPane(AppContext* _appContext);
@ -41,10 +39,8 @@ public:
void start() const override;
QString title() const override;
QString contentUrl() const override;
CompilationResult* result() const;
public slots:
void update();
};
}

2
mix/qml/CodeEditorView.qml

@ -43,7 +43,7 @@ Item {
editor.onEditorTextChanged.connect(function() {
documentEdit(document.documentId);
if (document.isContract)
codeModel.registerCodeChange(editor.getText());
codeModel.registerCodeChange(document.documentId, editor.getText());
});
editor.setText(data, document.syntaxMode);
}

2
mix/qml/Debugger.qml

@ -25,7 +25,7 @@ Rectangle {
function update(data, giveFocus)
{
if (statusPane && statusPane.result.successful)
if (statusPane && codeModel.hasContract)
{
Debugger.init(data);
debugScrollArea.visible = true;

2
mix/qml/MainContent.qml

@ -34,7 +34,7 @@ Rectangle {
onCompilationComplete: {
if (firstCompile) {
firstCompile = false;
if (codeModel.code.successful && runOnProjectLoad)
if (runOnProjectLoad)
startQuickDebugging();
}
}

21
mix/qml/ProjectList.qml

@ -101,14 +101,19 @@ Item {
Connections {
target: codeModel
onCompilationComplete: {
if (modelData === "Contracts")
{
var ctr = projectModel.listModel.get(0);
if (codeModel.code.contract.name !== ctr.name)
{
ctr.name = codeModel.code.contract.name;
projectModel.listModel.set(0, ctr);
sectionModel.set(0, ctr);
if (modelData === "Contracts") {
var ci = 0;
for (var si = 0; si < projectModel.listModel.count; si++) {
var document = projectModel.listModel.get(si);
if (document.isContract) {
var compiledDoc = codeModel.contractByDocumentId(document.documentId);
if (compiledDoc && compiledDoc.documentId === document.documentId && compiledDoc.contract.name !== document.name) {
document.name = compiledDoc.contract.name;
projectModel.listModel.set(si, document);
sectionModel.set(ci, document);
}
ci++;
}
}
}
}

2
mix/qml/ProjectModel.qml

@ -45,7 +45,7 @@ Item {
function newHtmlFile() { ProjectModelCode.newHtmlFile(); }
function newJsFile() { ProjectModelCode.newJsFile(); }
function newCssFile() { ProjectModelCode.newCssFile(); }
//function newContract() { ProjectModelCode.newContract(); }
function newContract() { ProjectModelCode.newContract(); }
function openDocument(documentId) { ProjectModelCode.openDocument(documentId); }
function openNextDocument() { ProjectModelCode.openNextDocument(); }
function openPrevDocument() { ProjectModelCode.openPrevDocument(); }

18
mix/qml/StateListModel.qml

@ -22,12 +22,12 @@ Item {
function fromPlainTransactionItem(t) {
var r = {
contractId: t.contractId,
functionId: t.functionId,
url: t.url,
value: QEtherHelper.createEther(t.value.value, t.value.unit),
gas: QEtherHelper.createBigInt(t.gas.value), //t.gas,//QEtherHelper.createEther(t.gas.value, t.gas.unit),
gasPrice: QEtherHelper.createEther(t.gasPrice.value, t.gasPrice.unit),
executeConstructor: t.executeConstructor,
stdContract: t.stdContract,
parameters: {}
};
@ -78,12 +78,12 @@ Item {
function toPlainTransactionItem(t) {
var r = {
contractId: t.contractId,
functionId: t.functionId,
url: t.url,
value: { value: t.value.value, unit: t.value.unit },
gas: { value: t.gas.value() },
gasPrice: { value: t.gasPrice.value, unit: t.gasPrice.unit },
executeConstructor: t.executeConstructor,
stdContract: t.stdContract,
parameters: {}
};
@ -159,7 +159,6 @@ Item {
value: QEtherHelper.createEther("100", QEther.Wei),
gas: QEtherHelper.createBigInt("125000"),
gasPrice: QEtherHelper.createEther("10000000000000", QEther.Wei),
executeConstructor: false,
stdContract: false
};
}
@ -178,16 +177,19 @@ Item {
var contractTransaction = defaultTransactionItem();
var contractItem = contractLibrary.model.get(i);
contractTransaction.url = contractItem.url;
contractTransaction.contractId = contractItem.name;
contractTransaction.functionId = contractItem.name;
contractTransaction.stdContract = true;
item.transactions.push(contractTransaction);
};
//add constructor
var ctorTr = defaultTransactionItem();
ctorTr.executeConstructor = true;
ctorTr.functionId = qsTr("Constructor");
item.transactions.push(ctorTr);
//add constructors, //TODO: order by dependencies
for(var c in codeModel.contracts) {
var ctorTr = defaultTransactionItem();
ctorTr.functionId = c;
ctorTr.contractId = c;
item.transactions.push(ctorTr);
}
return item;
}

14
mix/qml/StatusPane.qml

@ -8,9 +8,9 @@ Rectangle {
id: statusHeader
objectName: "statusPane"
function updateStatus()
function updateStatus(message)
{
if (statusPane.result.successful)
if (!message)
{
status.state = "";
status.text = qsTr("Compile without errors.");
@ -20,12 +20,12 @@ Rectangle {
else
{
status.state = "error";
var errorInfo = ErrorLocationFormater.extractErrorInfo(statusPane.result.compilerMessage, true);
var errorInfo = ErrorLocationFormater.extractErrorInfo(message, true);
status.text = errorInfo.errorLocation + " " + errorInfo.errorDetail;
logslink.visible = true;
debugImg.state = "";
}
debugRunActionIcon.enabled = statusPane.result.successful;
debugRunActionIcon.enabled = codeModel.hasContract;
}
function infoMessage(text)
@ -35,7 +35,6 @@ Rectangle {
logslink.visible = false;
}
Connections {
target:clientModel
onRunStarted: infoMessage(qsTr("Running transactions..."));
@ -49,6 +48,11 @@ Rectangle {
onDeploymentError: infoMessage(error);
onDeploymentComplete: infoMessage(qsTr("Deployment complete"));
}
Connections {
target: codeModel
onCompilationComplete: updateStatus();
onCompilationError: updateStatus(_error);
}
color: "transparent"
anchors.fill: parent

107
mix/qml/TransactionDialog.qml

@ -20,9 +20,9 @@ Window {
property alias gas: gasValueEdit.gasValue;
property alias gasPrice: gasPriceField.value;
property alias transactionValue: valueField.value;
property string contractId: contractComboBox.currentValue();
property alias functionId: functionComboBox.currentText;
property var itemParams;
property bool isConstructorTransaction;
property bool useTransactionDefaultValue: false
property var qType;
@ -39,32 +39,47 @@ Window {
gasValueEdit.gasValue = item.gas;
gasPriceField.value = item.gasPrice;
valueField.value = item.value;
var contractId = item.contractId;
var functionId = item.functionId;
isConstructorTransaction = item.executeConstructor;
rowFunction.visible = !item.executeConstructor;
rowFunction.visible = true;
itemParams = item.parameters !== undefined ? item.parameters : {};
functionsModel.clear();
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
contractComboBox.currentIndex = contractIndex;
loadFunctions(contractComboBox.currentValue());
var functionIndex = -1;
var functions = codeModel.code.contract.functions;
for (var f = 0; f < functions.length; f++) {
functionsModel.append({ text: functions[f].name });
if (functions[f].name === item.functionId)
for (var f = 0; f < functionsModel.count; f++)
if (functionsModel.get(f).text === item.functionId)
functionIndex = f;
}
if (functionIndex == -1 && functionsModel.count > 0)
functionIndex = 0; //@todo suggest unused function
functionComboBox.currentIndex = functionIndex;
paramsModel.clear();
if (!item.executeConstructor)
if (functionId !== contractComboBox.currentValue())
loadParameters();
else
{
var parameters = codeModel.code.contract.constructor.parameters;
for (var p = 0; p < parameters.length; p++)
loadParameter(parameters[p]);
else {
var contract = codeModel.contracts[contractId];
if (contract) {
var parameters = contract.contract.constructor.parameters;
for (var p = 0; p < parameters.length; p++)
loadParameter(parameters[p]);
}
}
modalTransactionDialog.setX((Screen.width - width) / 2);
modalTransactionDialog.setY((Screen.height - height) / 2);
@ -73,6 +88,21 @@ Window {
valueField.focus = true;
}
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++) {
functionsModel.append({ text: functions[f].name });
}
}
//append constructor
functionsModel.append({ text: contractId });
}
function loadParameter(parameter)
{
var type = parameter.type;
@ -104,10 +134,15 @@ Window {
if (!paramsModel)
return;
if (functionComboBox.currentIndex >= 0 && functionComboBox.currentIndex < functionsModel.count) {
var func = codeModel.code.contract.functions[functionComboBox.currentIndex];
var parameters = func.parameters;
for (var p = 0; p < parameters.length; p++)
loadParameter(parameters[p]);
var contract = codeModel.contracts[contractComboBox.currentValue()];
if (contract) {
var func = contract.contract.functions[functionComboBox.currentIndex];
if (func) {
var parameters = func.parameters;
for (var p = 0; p < parameters.length; p++)
loadParameter(parameters[p]);
}
}
}
}
@ -140,24 +175,21 @@ Window {
if (!useTransactionDefaultValue)
{
item = {
contractId: transactionDialog.contractId,
functionId: transactionDialog.functionId,
gas: transactionDialog.gas,
gasPrice: transactionDialog.gasPrice,
value: transactionDialog.transactionValue,
parameters: {},
executeConstructor: isConstructorTransaction
};
}
else
{
item = TransactionHelper.defaultTransaction();
item.contractId = transactionDialog.contractId;
item.functionId = transactionDialog.functionId;
item.executeConstructor = isConstructorTransaction;
}
if (isConstructorTransaction)
item.functionId = qsTr("Constructor");
var orderedQType = [];
for (var p = 0; p < transactionDialog.transactionParams.count; p++) {
var parameter = transactionDialog.transactionParams.get(p);
@ -178,6 +210,33 @@ Window {
id: dialogContent
anchors.top: parent.top
spacing: 10
RowLayout
{
id: rowContract
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Contract")
}
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: {
loadFunctions(currentValue());
}
}
}
RowLayout
{
id: rowFunction

12
mix/qml/WebPreview.qml

@ -29,7 +29,16 @@ Item {
}
function updateContract() {
webView.runJavaScript("updateContract(\"" + codeModel.code.contract.name + "\", \"" + clientModel.contractAddress + "\", " + codeModel.code.contractInterface + ")");
var contracts = {};
for (var c in codeModel.contracts) {
var contract = codeModel.contracts[c];
contracts[c] = {
name: contract.contract.name,
address: clientModel.contractAddresses[contract.contract.name],
interface: JSON.parse(contract.contractInterface),
};
}
webView.runJavaScript("updateContracts(" + JSON.stringify(contracts) + ")");
}
function reloadOnSave() {
@ -62,7 +71,6 @@ Item {
Connections {
target: clientModel
onContractAddressChanged: reload();
onRunComplete: reload();
}

16
mix/qml/html/WebContainer.html

@ -15,16 +15,18 @@ reloadPage = function() {
preview.contentWindow.location.reload();
};
updateContract = function(name, address, contractFace) {
updateContracts = function(contracts) {
if (window.web3) {
window.web3.provider.polls = [];
var contract = window.web3.eth.contract(address, contractFace);
window.contracts = {};
window.contracts[name] = {
address: address,
interface: contractFace,
contract: contract,
};
for (var c in contracts) {
var contract = window.web3.eth.contract(contracts[c].address, contracts[c].interface);
window.contracts[c] = {
address: c.address,
interface: c.interface,
contract: contract,
};
}
}
};

77
mix/qml/js/ProjectModel.js

@ -20,6 +20,9 @@
* Ethereum IDE client.
*/
var htmlTemplate = "<html>\n<head>\n<script>\n</script>\n</head>\n<body>\n<script>\n</script>\n</body>\n</html>";
var contractTemplate = "contract Contract {\n}\n";
function saveAll() {
saveProject();
}
@ -76,6 +79,16 @@ function loadProject(path) {
projectSettings.lastProjectPath = path;
projectLoading(projectData);
projectLoaded()
//TODO: move this to codemodel
var contractSources = {};
for (var d = 0; d < listModel.count; d++) {
var doc = listModel.get(d);
if (doc.isContract)
contractSources[doc.documentId] = fileIo.readFile(doc.path);
}
codeModel.reset(contractSources);
}
function addFile(fileName) {
@ -92,7 +105,7 @@ function addFile(fileName) {
contract: false,
path: p,
fileName: fileName,
name: isContract ? "Contract" : fileName,
name: fileName,
documentId: fileName,
syntaxMode: syntaxMode,
isText: isContract || isHtml || isCss || isJs,
@ -150,7 +163,7 @@ function openPrevDocument() {
}
function doCloseProject() {
console.log("closing project");
console.log("Closing project");
projectListModel.clear();
projectPath = "";
currentDocumentId = "";
@ -167,14 +180,14 @@ function doCreateProject(title, path) {
var projectFile = dirPath + projectFileName;
var indexFile = "index.html";
var contractsFile = "contracts.sol";
var contractsFile = "contract.sol";
var projectData = {
title: title,
files: [ contractsFile, indexFile ]
};
//TODO: copy from template
fileIo.writeFile(dirPath + indexFile, "<html>\n<head>\n<script>\nvar web3 = parent.web3;\nvar theContract = parent.contract;\n</script>\n</head>\n<body>\n<script>\n</script>\n</body>\n</html>");
fileIo.writeFile(dirPath + contractsFile, "contract Contract {\n}\n");
fileIo.writeFile(dirPath + indexFile, htmlTemplate);
fileIo.writeFile(dirPath + contractsFile, contractTemplate);
newProject(projectData);
var json = JSON.stringify(projectData, null, "\t");
fileIo.writeFile(projectFile, json);
@ -222,7 +235,7 @@ function removeDocument(documentId) {
}
function newHtmlFile() {
createAndAddFile("page", "html", "<html>\n</html>");
createAndAddFile("page", "html", htmlTemplate);
}
function newCssFile() {
@ -233,6 +246,11 @@ function newJsFile() {
createAndAddFile("script", "js", "function foo() {\n}\n");
}
function newContract() {
createAndAddFile("contract", "sol", contractTemplate);
}
function createAndAddFile(name, extension, content) {
var fileName = generateFileName(name, extension);
var filePath = projectPath + fileName;
@ -267,15 +285,21 @@ function deployProject(force) {
var jsonRpcUrl = "http://localhost:8080";
console.log("Deploying " + deploymentId + " to " + jsonRpcUrl);
deploymentStarted();
var code = codeModel.codeHex
var rpcRequest = JSON.stringify({
jsonrpc: "2.0",
method: "eth_transact",
params: [ {
"code": code
} ],
id: jsonRpcRequestId++
});
var requests = [];
var requestNames = [];
for (var c in codeModel.contracts) { //TODO: order based on dependencies
var code = codeModel.contracts[c].codeHex;
requests.push({
jsonrpc: "2.0",
method: "eth_transact",
params: [ { "code": code } ],
id: jsonRpcRequestId++
});
requestNames.push(c);
}
var rpcRequest = JSON.stringify(requests);;
var httpRequest = new XMLHttpRequest()
httpRequest.open("POST", jsonRpcUrl, true);
httpRequest.setRequestHeader("Content-type", "application/json");
@ -285,9 +309,12 @@ function deployProject(force) {
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200) {
var rpcResponse = JSON.parse(httpRequest.responseText);
var address = rpcResponse.result;
console.log("Created contract, address: " + address);
finalizeDeployment(deploymentId, address);
if (rpcResponse.length === requestNames.length) {
var contractAddresses = {};
for (var r = 0; r < rpcResponse.lenght; r++)
contractAddresses[requestNames[r]] = rpcResponse.result;
finalizeDeployment(deploymentId, contractAddresses);
}
} else {
var errorText = qsTr("Deployment error: RPC server HTTP status ") + httpRequest.status;
console.log(errorText);
@ -298,7 +325,7 @@ function deployProject(force) {
httpRequest.send(rpcRequest);
}
function finalizeDeployment(deploymentId, address) {
function finalizeDeployment(deploymentId, addresses) {
//create a dir for frontend files and copy them
var deploymentDir = projectPath + deploymentId + "/";
fileIo.makeDir(deploymentDir);
@ -326,16 +353,18 @@ function finalizeDeployment(deploymentId, address) {
fileIo.copyFile(doc.path, deploymentDir + doc.fileName);
}
//write deployment js
var contractAccessor = "contracts[\"" + codeModel.code.contract.name + "\"]";
var deploymentJs =
"// Autogenerated by Mix\n" +
"web3 = require(\"web3\");\n" +
"contracts = {};\n" +
contractAccessor + " = {\n" +
"\tinterface: " + codeModel.code.contractInterface + ",\n" +
"\taddress: \"" + address + "\"\n" +
"contracts = {};\n";
for (var c in codeModel.contracts) {
var contractAccessor = "contracts[\"" + codeModel.contracts[c].contract.name + "\"]";
deploymentJs += contractAccessor + " = {\n" +
"\tinterface: " + codeModel.contracts[c].contractInterface + ",\n" +
"\taddress: \"" + addresses[c] + "\"\n" +
"};\n" +
contractAccessor + ".contract = web3.eth.contract(" + contractAccessor + ".address, " + contractAccessor + ".interface);\n";
}
fileIo.writeFile(deploymentDir + "deployment.js", deploymentJs);
//copy scripts
fileIo.copyFile("qrc:///js/bignumber.min.js", deploymentDir + "bignumber.min.js");

1
mix/qml/js/TransactionHelper.js

@ -7,7 +7,6 @@ function defaultTransaction()
functionId: "",
gas: createBigInt("125000"),
gasPrice: createEther("100000", QEther.Wei),
executeConstructor: false,
parameters: {}
};
}

2
mix/qml/main.qml

@ -30,7 +30,7 @@ ApplicationWindow {
MenuItem { action: addNewHtmlFileAction }
MenuItem { action: addNewCssFileAction }
MenuSeparator {}
//MenuItem { action: addNewContractAction }
MenuItem { action: addNewContractAction }
MenuItem { action: closeProjectAction }
MenuSeparator {}
MenuItem { action: exitAppAction }

Loading…
Cancel
Save