', {
- css: {
- top: main.offset().top,
- left: main.offset().left
- }
- }).prependTo('body');
-
- $(document).mousemove(function (e) {
- ghostbar.css('left', e.pageX + 2);
- });
- });
-
- var $body = $('body');
-
- function setEditorSize (delta) {
- $('#righthand-panel').css('width', delta);
- $('#editor').css('right', delta);
- onResize();
- }
-
- function getEditorSize () {
- window.localStorage[EDITOR_SIZE_CACHE_KEY] = $('#righthand-panel').width();
- }
-
- $(document).mouseup(function (e) {
- if (dragging) {
- var delta = $body.width() - e.pageX + 2;
- $('#ghostbar').remove();
- $(document).unbind('mousemove');
- dragging = false;
- setEditorSize(delta);
- window.localStorage.setItem(EDITOR_SIZE_CACHE_KEY, delta);
- reAdjust();
- }
- });
-
- // set cached defaults
- var cachedSize = window.localStorage.getItem(EDITOR_SIZE_CACHE_KEY);
- if (cachedSize) setEditorSize(cachedSize);
- else getEditorSize();
-
-
- // ----------------- toggle right hand panel -----------------
-
- var hidingRHP = false;
- $('.toggleRHP').click(function () {
- hidingRHP = !hidingRHP;
- setEditorSize(hidingRHP ? 0 : window.localStorage[EDITOR_SIZE_CACHE_KEY]);
- $('.toggleRHP i').toggleClass('fa-angle-double-right', !hidingRHP);
- $('.toggleRHP i').toggleClass('fa-angle-double-left', hidingRHP);
- if (!hidingRHP) compiler.compile();
- });
-
- function getHidingRHP () { return hidingRHP; }
-
- // ----------------- editor resize ---------------
-
- function onResize () {
- editor.resize();
- reAdjust();
- }
- window.onresize = onResize;
- onResize();
-
- document.querySelector('#editor').addEventListener('change', onResize);
- document.querySelector('#editorWrap').addEventListener('change', onResize);
-
-
- // ----------------- compiler output renderer ----------------------
-
- $('.asmOutput button').click(function () { $(this).parent().find('pre').toggle(); });
-
-
- // ----------------- compiler ----------------------
-
- function handleGithubCall (root, path, cb) {
- $('#output').append($('
').text('Loading github.com/' + root + '/' + path + ' ...')));
- return $.getJSON('https://api.github.com/repos/' + root + '/contents/' + path, cb);
- }
-
- var compiler = new Compiler(editor, handleGithubCall, $('#output'), getHidingRHP, updateFiles);
-
- function setVersionText (text) {
- $('#version').text(text);
- }
-
- var loadVersion = function (version) {
- setVersionText('(loading)');
- queryParams.update({version: version});
- var isFirefox = typeof InstallTrigger !== 'undefined';
- if (document.location.protocol !== 'file:' && Worker !== undefined && isFirefox) {
- // Workers cannot load js on "file:"-URLs and we get a
- // "Uncaught RangeError: Maximum call stack size exceeded" error on Chromium,
- // resort to non-worker version in that case.
- compiler.loadVersion(true, version, setVersionText);
- } else {
- compiler.loadVersion(false, version, setVersionText);
- }
- };
-
- loadVersion(queryParams.get().version || 'soljson-latest.js');
-
- document.querySelector('#optimize').addEventListener('change', function () {
- queryParams.update({optimize: document.querySelector('#optimize').checked });
- compiler.compile();
- });
+ });
+
+ // ----------------- file upload -------------
+
+ $('.inputFile').on('change', function () {
+ var fileList = $('input.inputFile')[0].files;
+ for (var i = 0; i < fileList.length; i++) {
+ var name = fileList[i].name;
+ if (!window.localStorage[utils.fileKey(name)] || confirm('The file ' + name + ' already exists! Would you like to overwrite it?')) {
+ editor.uploadFile(fileList[i]);
+ updateFiles();
+ }
+ }
+
+ $filesEl.animate({ left: Math.max((0 - activeFilePos() + (FILE_SCROLL_DELTA / 2)), 0) + 'px' }, 'slow', function () {
+ reAdjust();
+ });
+ });
+
+ $filesEl.on('click', '.file:not(.active)', showFileHandler);
+
+ $filesEl.on('click', '.file.active', function (ev) {
+ var $fileTabEl = $(this);
+ var originalName = $fileTabEl.find('.name').text();
+ ev.preventDefault();
+ if ($(this).find('input').length > 0) return false;
+ var $fileNameInputEl = $('
');
+ $fileTabEl.html($fileNameInputEl);
+ $fileNameInputEl.focus();
+ $fileNameInputEl.select();
+ $fileNameInputEl.on('blur', handleRename);
+ $fileNameInputEl.keyup(handleRename);
+
+ function handleRename (ev) {
+ ev.preventDefault();
+ if (ev.which && ev.which !== 13) return false;
+ var newName = ev.target.value;
+ $fileNameInputEl.off('blur');
+ $fileNameInputEl.off('keyup');
+
+ if (newName !== originalName && confirm('Are you sure you want to rename: ' + originalName + ' to ' + newName + '?')) {
+ var content = window.localStorage.getItem(utils.fileKey(originalName));
+ window.localStorage[utils.fileKey(newName)] = content;
+ window.localStorage.removeItem(utils.fileKey(originalName));
+ editor.setCacheFile(newName);
+ }
+
+ updateFiles();
+ return false;
+ }
+
+ return false;
+ });
+
+ $filesEl.on('click', '.file .remove', function (ev) {
+ ev.preventDefault();
+ var name = $(this).parent().find('.name').text();
+
+ if (confirm('Are you sure you want to remove: ' + name + ' from local storage?')) {
+ window.localStorage.removeItem(utils.fileKey(name));
+ editor.setNextFile(utils.fileKey(name));
+ updateFiles();
+ }
+ return false;
+ });
+
+ function showFileHandler (ev) {
+ ev.preventDefault();
+ editor.setCacheFile($(this).find('.name').text());
+ updateFiles();
+ return false;
+ }
+
+ function activeFileTab () {
+ var name = editor.getCacheFile();
+ return $('#files .file').filter(function () { return $(this).find('.name').text() === name; });
+ }
+
+ function updateFiles () {
+ var $filesEl = $('#files');
+ var files = editor.getFiles();
+
+ $filesEl.find('.file').remove();
+
+ for (var f in files) {
+ $filesEl.append(fileTabTemplate(files[f]));
+ }
+
+ if (editor.cacheFileIsPresent()) {
+ var active = activeFileTab();
+ active.addClass('active');
+ editor.resetSession();
+ }
+ $('#input').toggle(editor.cacheFileIsPresent());
+ $('#output').toggle(editor.cacheFileIsPresent());
+ reAdjust();
+ }
+
+ function fileTabTemplate (key) {
+ var name = utils.fileNameFromKey(key);
+ return $('
');
+ }
+
+ var $filesWrapper = $('.files-wrapper');
+ var $scrollerRight = $('.scroller-right');
+ var $scrollerLeft = $('.scroller-left');
+
+ function widthOfList () {
+ var itemsWidth = 0;
+ $('.file').each(function () {
+ var itemWidth = $(this).outerWidth();
+ itemsWidth += itemWidth;
+ });
+ return itemsWidth;
+ }
+
+ // function widthOfHidden () {
+ // return ($filesWrapper.outerWidth() - widthOfList() - getLeftPosi());
+ // }
+
+ function widthOfVisible () {
+ return $filesWrapper.outerWidth();
+ }
+
+ function getLeftPosi () {
+ return $filesEl.position().left;
+ }
+
+ function activeFilePos () {
+ var el = $filesEl.find('.active');
+ var l = el.position().left;
+ return l;
+ }
+
+ function reAdjust () {
+ if (widthOfList() + getLeftPosi() > widthOfVisible()) {
+ $scrollerRight.fadeIn('fast');
+ } else {
+ $scrollerRight.fadeOut('fast');
+ }
+
+ if (getLeftPosi() < 0) {
+ $scrollerLeft.fadeIn('fast');
+ } else {
+ $scrollerLeft.fadeOut('fast');
+ $filesEl.animate({ left: getLeftPosi() + 'px' }, 'slow');
+ }
+ }
+
+ $scrollerRight.click(function () {
+ var delta = (getLeftPosi() - FILE_SCROLL_DELTA);
+ $filesEl.animate({ left: delta + 'px' }, 'slow', function () {
+ reAdjust();
+ });
+ });
- storageHandler.sync();
+ $scrollerLeft.click(function () {
+ var delta = Math.min((getLeftPosi() + FILE_SCROLL_DELTA), 0);
+ $filesEl.animate({ left: delta + 'px' }, 'slow', function () {
+ reAdjust();
+ });
+ });
+
+ updateFiles();
+
+ // ----------------- version selector-------------
+
+ // var soljsonSources is provided by bin/list.js
+
+ $('option', '#versionSelector').remove();
+ $.each(soljsonSources, function (i, file) {
+ if (file) {
+ var version = file.replace(/soljson-(.*).js/, '$1');
+ $('#versionSelector').append(new Option(version, file));
+ }
+ });
+ $('#versionSelector').change(function () {
+ queryParams.update({ version: $('#versionSelector').val() });
+ loadVersion($('#versionSelector').val());
+ });
+
+ // ----------------- resizeable ui ---------------
+
+ var EDITOR_SIZE_CACHE_KEY = 'editor-size-cache';
+ var dragging = false;
+ $('#dragbar').mousedown(function (e) {
+ e.preventDefault();
+ dragging = true;
+ var main = $('#righthand-panel');
+ var ghostbar = $('
', {
+ css: {
+ top: main.offset().top,
+ left: main.offset().left
+ }
+ }).prependTo('body');
+
+ $(document).mousemove(function (e) {
+ ghostbar.css('left', e.pageX + 2);
+ });
+ });
+
+ var $body = $('body');
+
+ function setEditorSize (delta) {
+ $('#righthand-panel').css('width', delta);
+ $('#editor').css('right', delta);
+ onResize();
+ }
+
+ function getEditorSize () {
+ window.localStorage[EDITOR_SIZE_CACHE_KEY] = $('#righthand-panel').width();
+ }
+
+ $(document).mouseup(function (e) {
+ if (dragging) {
+ var delta = $body.width() - e.pageX + 2;
+ $('#ghostbar').remove();
+ $(document).unbind('mousemove');
+ dragging = false;
+ setEditorSize(delta);
+ window.localStorage.setItem(EDITOR_SIZE_CACHE_KEY, delta);
+ reAdjust();
+ }
+ });
+
+ // set cached defaults
+ var cachedSize = window.localStorage.getItem(EDITOR_SIZE_CACHE_KEY);
+ if (cachedSize) setEditorSize(cachedSize);
+ else getEditorSize();
+
+ // ----------------- toggle right hand panel -----------------
+
+ var hidingRHP = false;
+ $('.toggleRHP').click(function () {
+ hidingRHP = !hidingRHP;
+ setEditorSize(hidingRHP ? 0 : window.localStorage[EDITOR_SIZE_CACHE_KEY]);
+ $('.toggleRHP i').toggleClass('fa-angle-double-right', !hidingRHP);
+ $('.toggleRHP i').toggleClass('fa-angle-double-left', hidingRHP);
+ if (!hidingRHP) compiler.compile();
+ });
+
+ function getHidingRHP () { return hidingRHP; }
+
+ // ----------------- editor resize ---------------
+
+ function onResize () {
+ editor.resize();
+ reAdjust();
+ }
+ window.onresize = onResize;
+ onResize();
+
+ document.querySelector('#editor').addEventListener('change', onResize);
+ document.querySelector('#editorWrap').addEventListener('change', onResize);
+
+ // ----------------- compiler output renderer ----------------------
+
+ $('.asmOutput button').click(function () { $(this).parent().find('pre').toggle(); });
+
+ // ----------------- compiler ----------------------
+
+ function handleGithubCall (root, path, cb) {
+ $('#output').append($('
').append($('
').text('Loading github.com/' + root + '/' + path + ' ...')));
+ return $.getJSON('https://api.github.com/repos/' + root + '/contents/' + path, cb);
+ }
+
+ var compiler = new Compiler(editor, handleGithubCall, $('#output'), getHidingRHP, updateFiles);
+
+ function setVersionText (text) {
+ $('#version').text(text);
+ }
+
+ var loadVersion = function (version) {
+ setVersionText('(loading)');
+ queryParams.update({version: version});
+ var isFirefox = typeof InstallTrigger !== 'undefined';
+ if (document.location.protocol !== 'file:' && Worker !== undefined && isFirefox) {
+ // Workers cannot load js on "file:"-URLs and we get a
+ // "Uncaught RangeError: Maximum call stack size exceeded" error on Chromium,
+ // resort to non-worker version in that case.
+ compiler.loadVersion(true, version, setVersionText);
+ } else {
+ compiler.loadVersion(false, version, setVersionText);
+ }
+ };
+
+ loadVersion(queryParams.get().version || 'soljson-latest.js');
+
+ document.querySelector('#optimize').addEventListener('change', function () {
+ queryParams.update({ optimize: document.querySelector('#optimize').checked });
+ compiler.compile();
+ });
+
+ storageHandler.sync();
};
module.exports = {
- 'run': run
+ 'run': run
};
diff --git a/src/app/compiler-worker.js b/src/app/compiler-worker.js
index ca1cb890f3..ffbd332b2d 100644
--- a/src/app/compiler-worker.js
+++ b/src/app/compiler-worker.js
@@ -1,42 +1,41 @@
-var version = function() { return '(loading)'; }
-var compileJSON = function() { return ''; }
+var version = function () { return '(loading)'; };
+var compileJSON = function () { return ''; };
var missingInputs = [];
module.exports = function (self) {
- self.addEventListener('message', function(e) {
- var data = e.data;
- switch (data.cmd) {
- case 'loadVersion':
- delete Module;
- version = null;
- compileJSON = null;
+ self.addEventListener('message', function (e) {
+ var data = e.data;
+ switch (data.cmd) {
+ case 'loadVersion':
+ delete Module;
+ version = null;
+ compileJSON = null;
- importScripts(data.data);
- version = Module.cwrap("version", "string", []);
- if ('_compileJSONCallback' in Module)
- {
- compileJSONInternal = Module.cwrap("compileJSONCallback", "string", ["string", "number", "number"]);
- var missingInputCallback = Module.Runtime.addFunction(function(path) {
- missingInputs.push(Module.Pointer_stringify(path));
- });
- compileJSON = function(input, optimize) {
- return compileJSONInternal(input, optimize, missingInputCallback);
- };
- }
- else if ('_compileJSONMulti' in Module)
- compileJSON = Module.cwrap("compileJSONMulti", "string", ["string", "number"]);
- else
- compileJSON = Module.cwrap("compileJSON", "string", ["string", "number"]);
- postMessage({
- cmd: 'versionLoaded',
- data: version(),
- acceptsMultipleFiles: ('_compileJSONMulti' in Module)
- });
- break;
- case 'compile':
- missingInputs.length = 0;
- postMessage({cmd: 'compiled', data: compileJSON(data.source, data.optimize), missingInputs: missingInputs});
- break;
- }
- }, false);
-}
+ importScripts(data.data);
+ version = Module.cwrap('version', 'string', []);
+ if ('_compileJSONCallback' in Module) {
+ var compileJSONInternal = Module.cwrap('compileJSONCallback', 'string', ['string', 'number', 'number']);
+ var missingInputCallback = Module.Runtime.addFunction(function (path) {
+ missingInputs.push(Module.Pointer_stringify(path));
+ });
+ compileJSON = function (input, optimize) {
+ return compileJSONInternal(input, optimize, missingInputCallback);
+ };
+ } else if ('_compileJSONMulti' in Module) {
+ compileJSON = Module.cwrap('compileJSONMulti', 'string', ['string', 'number']);
+ } else {
+ compileJSON = Module.cwrap('compileJSON', 'string', ['string', 'number']);
+ }
+ postMessage({
+ cmd: 'versionLoaded',
+ data: version(),
+ acceptsMultipleFiles: ('_compileJSONMulti' in Module)
+ });
+ break;
+ case 'compile':
+ missingInputs.length = 0;
+ postMessage({cmd: 'compiled', data: compileJSON(data.source, data.optimize), missingInputs: missingInputs});
+ break;
+ }
+ }, false);
+};
diff --git a/src/app/compiler.js b/src/app/compiler.js
index eea4294ec9..e65be5c142 100644
--- a/src/app/compiler.js
+++ b/src/app/compiler.js
@@ -42,7 +42,7 @@ function Compiler (editor, handleGithubCall, outputField, hidingRHP, updateFiles
sourceAnnotations = [];
outputField.empty();
var input = editor.getValue();
- editor.setCacheFileContent(input)
+ editor.setCacheFileContent(input);
var files = {};
files[editor.getCacheFile()] = input;
@@ -148,7 +148,7 @@ function Compiler (editor, handleGithubCall, outputField, hidingRHP, updateFiles
function loadInternal (url, setVersionText) {
Module = null;
// Set a safe fallback until the new one is loaded
- compileJSON = function(source, optimize) { compilationFinished('{}'); };
+ compileJSON = function (source, optimize) { compilationFinished('{}'); };
var newScript = document.createElement('script');
newScript.type = 'text/javascript';
@@ -171,14 +171,14 @@ function Compiler (editor, handleGithubCall, outputField, hidingRHP, updateFiles
worker.addEventListener('message', function (msg) {
var data = msg.data;
switch (data.cmd) {
- case 'versionLoaded':
- compilerAcceptsMultipleFiles = !!data.acceptsMultipleFiles;
- onCompilerLoaded(setVersionText, data.data);
- break;
- case 'compiled':
- compilationFinished(data.data, data.missingInputs);
- break;
- };
+ case 'versionLoaded':
+ compilerAcceptsMultipleFiles = !!data.acceptsMultipleFiles;
+ onCompilerLoaded(setVersionText, data.data);
+ break;
+ case 'compiled':
+ compilationFinished(data.data, data.missingInputs);
+ break;
+ }
});
worker.onerror = function (msg) { console.log(msg.data); };
worker.addEventListener('error', function (msg) { console.log(msg.data); });
@@ -186,7 +186,7 @@ function Compiler (editor, handleGithubCall, outputField, hidingRHP, updateFiles
worker.postMessage({cmd: 'compile', source: source, optimize: optimize});
};
worker.postMessage({cmd: 'loadVersion', data: url});
- };
+ }
function gatherImports (files, importHints, cb) {
importHints = importHints || [];
@@ -201,7 +201,7 @@ function Compiler (editor, handleGithubCall, outputField, hidingRHP, updateFiles
reloop = false;
for (var fileName in files) {
var match;
- while (match = importRegex.exec(files[fileName])) {
+ while ((match = importRegex.exec(files[fileName]))) {
importHints.push(match[1]);
}
}
@@ -219,7 +219,7 @@ function Compiler (editor, handleGithubCall, outputField, hidingRHP, updateFiles
} else if (m in cachedRemoteFiles) {
files[m] = cachedRemoteFiles[m];
reloop = true;
- } else if (githubMatch = /^(https?:\/\/)?(www.)?github.com\/([^\/]*\/[^\/]*)\/(.*)/.exec(m)) {
+ } else if ((githubMatch = /^(https?:\/\/)?(www.)?github.com\/([^\/]*\/[^\/]*)\/(.*)/.exec(m))) {
handleGithubCall(githubMatch[3], githubMatch[4], function (result) {
if ('content' in result) {
var content = Base64.decode(result.content);
@@ -243,4 +243,4 @@ function Compiler (editor, handleGithubCall, outputField, hidingRHP, updateFiles
}
}
-module.exports = Compiler
+module.exports = Compiler;
diff --git a/src/app/editor.js b/src/app/editor.js
index b8fe36d184..4592525d75 100644
--- a/src/app/editor.js
+++ b/src/app/editor.js
@@ -1,3 +1,5 @@
+/* global BALLOT_EXAMPLE, FileReader */
+
var utils = require('./utils');
var ace = require('brace');
diff --git a/src/app/execution-context.js b/src/app/execution-context.js
index db2c1cde8f..033f91ffdc 100644
--- a/src/app/execution-context.js
+++ b/src/app/execution-context.js
@@ -1,3 +1,5 @@
+/* global confirm */
+
var $ = require('jquery');
var Web3 = require('web3');
diff --git a/src/app/gist-handler.js b/src/app/gist-handler.js
index fe21092e73..7d07651512 100644
--- a/src/app/gist-handler.js
+++ b/src/app/gist-handler.js
@@ -1,3 +1,5 @@
+/* global prompt */
+
var queryParams = require('./query-params');
function handleLoad (cb) {
diff --git a/src/app/query-params.js b/src/app/query-params.js
index 025b45ee98..391a242195 100644
--- a/src/app/query-params.js
+++ b/src/app/query-params.js
@@ -34,5 +34,5 @@ function updateQueryParams (params) {
module.exports = {
get: getQueryParams,
- update: updateQueryParams,
-};
\ No newline at end of file
+ update: updateQueryParams
+};
diff --git a/src/app/renderer.js b/src/app/renderer.js
index 6b727f73b5..b852390b51 100644
--- a/src/app/renderer.js
+++ b/src/app/renderer.js
@@ -6,7 +6,6 @@ var utils = require('./utils');
var ExecutionContext = require('./execution-context');
function Renderer (editor, compiler, updateFiles) {
-
var detailsOpen = {};
var executionContext = new ExecutionContext(compiler);
@@ -33,7 +32,7 @@ function Renderer (editor, compiler, updateFiles) {
// Switch to file
editor.setCacheFile(errFile);
updateFiles();
- // @TODO could show some error icon in files with errors
+ // @TODO could show some error icon in files with errors
}
editor.handleErrorClick(errLine, errCol);
});
@@ -43,7 +42,7 @@ function Renderer (editor, compiler, updateFiles) {
return false;
});
}
- };
+ }
this.error = renderError;
var combined = function (contractName, jsonInterface, bytecode) {
@@ -51,7 +50,6 @@ function Renderer (editor, compiler, updateFiles) {
};
function renderContracts (data, source) {
-
var udappContracts = [];
for (var contractName in data.contracts) {
var contract = data.contracts[contractName];
@@ -105,7 +103,7 @@ function Renderer (editor, compiler, updateFiles) {
$contractOutput.find('.title').click(function (ev) { $(this).closest('.contract').toggleClass('hide'); });
$('#output').append($contractOutput);
$('.col2 input,textarea').click(function () { this.select(); });
- };
+ }
this.contracts = renderContracts;
var tableRowItems = function (first, second, cls) {
@@ -132,8 +130,9 @@ function Renderer (editor, compiler, updateFiles) {
.append(tableRow('Solidity Interface', contract.solidity_interface))
.append(tableRow('Opcodes', contract.opcodes));
var funHashes = '';
- for (var fun in contract.functionHashes)
+ for (var fun in contract.functionHashes) {
funHashes += contract.functionHashes[fun] + ' ' + fun + '\n';
+ }
details.append($('
Functions'));
details.append($('
').text(funHashes));
details.append($('
Gas Estimates'));
@@ -154,7 +153,7 @@ function Renderer (editor, compiler, updateFiles) {
};
var formatGasEstimates = function (data) {
- var gasToText = function (g) { return g === null ? 'unknown' : g; }
+ var gasToText = function (g) { return g === null ? 'unknown' : g; };
var text = '';
var fun;
if ('creation' in data) {
@@ -217,16 +216,15 @@ function Renderer (editor, compiler, updateFiles) {
});
code += '\n {' +
- '\n from: web3.eth.accounts[0], ' +
- '\n data: \'' + bytecode + '\', ' +
- '\n gas: 3000000' +
- '\n }, function (e, contract){' +
- '\n console.log(e, contract);' +
- '\n if (typeof contract.address !== \'undefined\') {' +
- '\n console.log(\'Contract mined! address: \' + contract.address + \' transactionHash: \' + contract.transactionHash);' +
- '\n }' +
- '\n })';
-
+ '\n from: web3.eth.accounts[0], ' +
+ '\n data: \'' + bytecode + '\', ' +
+ '\n gas: 3000000' +
+ '\n }, function (e, contract){' +
+ '\n console.log(e, contract);' +
+ '\n if (typeof contract.address !== \'undefined\') {' +
+ '\n console.log(\'Contract mined! address: \' + contract.address + \' transactionHash: \' + contract.transactionHash);' +
+ '\n }' +
+ '\n })';
return code;
}
@@ -241,7 +239,6 @@ function Renderer (editor, compiler, updateFiles) {
}
return funABI;
}
-
}
module.exports = Renderer;
diff --git a/src/app/storage-handler.js b/src/app/storage-handler.js
index 783dce1778..805bc4936b 100644
--- a/src/app/storage-handler.js
+++ b/src/app/storage-handler.js
@@ -1,17 +1,16 @@
+/* global chrome, confirm, localStorage */
+
var utils = require('./utils');
function StorageHandler (updateFiles) {
-
this.sync = function () {
-
if (typeof chrome === 'undefined' || !chrome || !chrome.storage || !chrome.storage.sync) {
return;
}
var obj = {};
var done = false;
- var count = 0
- var dont = 0;
+ var count = 0;
function check (key) {
chrome.storage.sync.get(key, function (resp) {
@@ -28,9 +27,9 @@ function StorageHandler (updateFiles) {
if (done >= count) {
chrome.storage.sync.set(obj, function () {
console.log('updated cloud files with: ', obj, this, arguments);
- })
+ });
}
- })
+ });
}
for (var y in window.localStorage) {
diff --git a/src/index.js b/src/index.js
index 6fb4e1b317..d77b8385d1 100644
--- a/src/index.js
+++ b/src/index.js
@@ -2,4 +2,4 @@ require('es6-shim');
var app = require('./app.js');
var $ = require('jquery');
-$(document).ready(function () { app.run(); });
\ No newline at end of file
+$(document).ready(function () { app.run(); });
diff --git a/src/universal-dapp.js b/src/universal-dapp.js
index 867ac65194..7a9658c895 100644
--- a/src/universal-dapp.js
+++ b/src/universal-dapp.js
@@ -1,3 +1,5 @@
+/* global prompt */
+
var $ = require('jquery');
var EthJSVM = require('ethereumjs-vm');
var ethJSUtil = require('ethereumjs-util');
@@ -7,663 +9,661 @@ var EthJSBlock = require('ethereumjs-block');
var BN = ethJSUtil.BN;
function UniversalDApp (contracts, options) {
- this.options = options || {};
- this.$el = $('
');
- this.contracts = contracts;
- this.renderOutputModifier = options.renderOutputModifier || function (name, content) { return content; };
+ this.options = options || {};
+ this.$el = $('
');
+ this.contracts = contracts;
+ this.renderOutputModifier = options.renderOutputModifier || function (name, content) { return content; };
- this.web3 = options.web3;
+ this.web3 = options.web3;
- if (options.mode === 'vm') {
- // FIXME: use `options.vm` or `this.vm` consistently
- options.vm = true;
+ if (options.mode === 'vm') {
+ // FIXME: use `options.vm` or `this.vm` consistently
+ options.vm = true;
- this.accounts = {};
+ this.accounts = {};
- this.vm = new EthJSVM(null, null, { activatePrecompiles: true, enableHomestead: true });
+ this.vm = new EthJSVM(null, null, { activatePrecompiles: true, enableHomestead: true });
- this.addAccount('3cd7232cd6f3fc66a57a6bedc1a8ed6c228fff0a327e169c2bcc5e869ed49511');
- this.addAccount('2ac6c190b09897cd8987869cc7b918cfea07ee82038d492abce033c75c1b1d0c');
- } else if (options.mode !== 'web3') {
- throw new Error('Either VM or Web3 mode must be selected');
- }
+ this.addAccount('3cd7232cd6f3fc66a57a6bedc1a8ed6c228fff0a327e169c2bcc5e869ed49511');
+ this.addAccount('2ac6c190b09897cd8987869cc7b918cfea07ee82038d492abce033c75c1b1d0c');
+ } else if (options.mode !== 'web3') {
+ throw new Error('Either VM or Web3 mode must be selected');
+ }
}
UniversalDApp.prototype.addAccount = function (privateKey, balance) {
- if (this.accounts) {
- privateKey = new Buffer(privateKey, 'hex');
- var address = ethJSUtil.privateToAddress(privateKey);
+ if (this.accounts) {
+ privateKey = new Buffer(privateKey, 'hex');
+ var address = ethJSUtil.privateToAddress(privateKey);
- // FIXME: we don't care about the callback, but we should still make this proper
- this.vm.stateManager.putAccountBalance(address, balance || 'f00000000000000001', function cb () {});
+ // FIXME: we don't care about the callback, but we should still make this proper
+ this.vm.stateManager.putAccountBalance(address, balance || 'f00000000000000001', function cb () {});
- this.accounts['0x' + address.toString('hex')] = { privateKey: privateKey, nonce: 0 };
- }
+ this.accounts['0x' + address.toString('hex')] = { privateKey: privateKey, nonce: 0 };
+ }
};
UniversalDApp.prototype.getAccounts = function (cb) {
- if (!this.vm) {
- this.web3.eth.getAccounts(cb);
- } else {
- if (!this.accounts) {
- return cb('No accounts?');
- }
-
- cb(null, Object.keys(this.accounts));
+ if (!this.vm) {
+ this.web3.eth.getAccounts(cb);
+ } else {
+ if (!this.accounts) {
+ return cb('No accounts?');
}
+
+ cb(null, Object.keys(this.accounts));
+ }
};
UniversalDApp.prototype.getBalance = function (address, cb) {
- address = ethJSUtil.stripHexPrefix(address);
-
- if (!this.vm) {
- this.web3.eth.getBalance(address, function (err, res) {
- if (err) {
- cb(err);
- } else {
- cb(null, res.toString(10));
- }
- });
- } else {
- if (!this.accounts) {
- return cb('No accounts?');
- }
-
- this.vm.stateManager.getAccountBalance(new Buffer(address, 'hex'), function (err, res) {
- if (err) {
- cb('Account not found');
- } else {
- cb(null, new BN(res).toString(10));
- }
- });
+ address = ethJSUtil.stripHexPrefix(address);
+
+ if (!this.vm) {
+ this.web3.eth.getBalance(address, function (err, res) {
+ if (err) {
+ cb(err);
+ } else {
+ cb(null, res.toString(10));
+ }
+ });
+ } else {
+ if (!this.accounts) {
+ return cb('No accounts?');
}
+
+ this.vm.stateManager.getAccountBalance(new Buffer(address, 'hex'), function (err, res) {
+ if (err) {
+ cb('Account not found');
+ } else {
+ cb(null, new BN(res).toString(10));
+ }
+ });
+ }
};
UniversalDApp.prototype.render = function () {
- if (this.contracts.length === 0) {
- this.$el.append(this.getABIInputForm());
- } else {
-
- for (var c in this.contracts) {
- var $contractEl = $('
');
-
- if (this.contracts[c].address) {
- this.getInstanceInterface(this.contracts[c], this.contracts[c].address, $contractEl);
- } else {
- var $title = $('
').text(this.contracts[c].name);
- if (this.contracts[c].bytecode) {
- $title.append($('
').text((this.contracts[c].bytecode.length / 2) + ' bytes'));
- }
- $contractEl.append($title).append(this.getCreateInterface($contractEl, this.contracts[c]));
- }
- this.$el.append(this.renderOutputModifier(this.contracts[c].name, $contractEl));
+ if (this.contracts.length === 0) {
+ this.$el.append(this.getABIInputForm());
+ } else {
+ for (var c in this.contracts) {
+ var $contractEl = $('
');
+
+ if (this.contracts[c].address) {
+ this.getInstanceInterface(this.contracts[c], this.contracts[c].address, $contractEl);
+ } else {
+ var $title = $('
').text(this.contracts[c].name);
+ if (this.contracts[c].bytecode) {
+ $title.append($('
').text((this.contracts[c].bytecode.length / 2) + ' bytes'));
}
+ $contractEl.append($title).append(this.getCreateInterface($contractEl, this.contracts[c]));
+ }
+ this.$el.append(this.renderOutputModifier(this.contracts[c].name, $contractEl));
}
- var $legend = $('
')
- .append($('
').text('Attach'))
- .append($('
').text('Transact'))
- .append($('
').text('Call'));
+ }
+ var $legend = $('
')
+ .append($('
').text('Attach'))
+ .append($('
').text('Transact'))
+ .append($('
').text('Call'));
- this.$el.append($('
')
- .html('
Universal ÐApp powered by The Blockchain'));
+ this.$el.append($('
')
+ .html('
Universal ÐApp powered by The Blockchain'));
- this.$el.append($legend);
- return this.$el;
+ this.$el.append($legend);
+ return this.$el;
};
UniversalDApp.prototype.getContractByName = function (contractName) {
- for (var c in this.contracts) {
- if (this.contracts[c].name === contractName) {
- return this.contracts[c];
- }
+ for (var c in this.contracts) {
+ if (this.contracts[c].name === contractName) {
+ return this.contracts[c];
}
- return null;
+ }
+ return null;
};
UniversalDApp.prototype.getABIInputForm = function (cb) {
- var self = this;
- var $el = $('
');
- var $jsonInput = $('
');
- var $createButton = $('
').text('Create a Universal ÐApp');
- $createButton.click(function (ev) {
- var contracts = $.parseJSON($jsonInput.val());
- if (cb) {
- var err = null;
- var dapp = null;
- try {
- dapp = new UniversalDApp(contracts, self.options);
- } catch (e) {
- err = e;
- }
- cb(err, dapp);
- } else {
- self.contracts = contracts;
- self.$el.empty().append(self.render());
- }
- });
- $el.append($jsonInput).append($createButton);
- return $el;
+ var self = this;
+ var $el = $('
');
+ var $jsonInput = $('
');
+ var $createButton = $('
').text('Create a Universal ÐApp');
+ $createButton.click(function (ev) {
+ var contracts = $.parseJSON($jsonInput.val());
+ if (cb) {
+ var err = null;
+ var dapp = null;
+ try {
+ dapp = new UniversalDApp(contracts, self.options);
+ } catch (e) {
+ err = e;
+ }
+ cb(err, dapp);
+ } else {
+ self.contracts = contracts;
+ self.$el.empty().append(self.render());
+ }
+ });
+ $el.append($jsonInput).append($createButton);
+ return $el;
};
-
UniversalDApp.prototype.getCreateInterface = function ($container, contract) {
- var self = this;
- var $createInterface = $('
');
- if (this.options.removable) {
- var $close = $('
');
- $close.click(function () { self.$el.remove(); });
- $createInterface.append($close);
- }
- var $newButton = this.getInstanceInterface(contract);
- var $atButton = $('
').text('At Address').click(function () { self.clickContractAt(self, $container.find('.createContract'), contract); });
- $createInterface.append($atButton).append($newButton);
- return $createInterface;
+ var self = this;
+ var $createInterface = $('
');
+ if (this.options.removable) {
+ var $close = $('
');
+ $close.click(function () { self.$el.remove(); });
+ $createInterface.append($close);
+ }
+ var $newButton = this.getInstanceInterface(contract);
+ var $atButton = $('
').text('At Address').click(function () { self.clickContractAt(self, $container.find('.createContract'), contract); });
+ $createInterface.append($atButton).append($newButton);
+ return $createInterface;
};
UniversalDApp.prototype.getInstanceInterface = function (contract, address, $target) {
- var self = this;
- var abi = JSON.parse(contract.interface).sort(function (a, b) {
- if (a.name > b.name) {
- return -1;
- } else {
- return 1;
- }
- }).sort(function (a, b) {
- if (a.constant === true) {
- return -1;
- } else {
- return 1;
- }
+ var self = this;
+ var abi = JSON.parse(contract.interface).sort(function (a, b) {
+ if (a.name > b.name) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }).sort(function (a, b) {
+ if (a.constant === true) {
+ return -1;
+ } else {
+ return 1;
+ }
+ });
+ var funABI = this.getConstructorInterface(abi);
+ var $createInterface = $('
');
+
+ var appendFunctions = function (address, $el) {
+ var $instance = $('
');
+ if (self.options.removable_instances) {
+ var $close = $('
');
+ $close.click(function () { $instance.remove(); });
+ $instance.append($close);
+ }
+ var context = self.options.vm ? 'memory' : 'blockchain';
+ var $title = $('
').text(contract.name + ' at ' + (self.options.vm ? '0x' : '') + address.toString('hex') + ' (' + context + ')');
+ $title.click(function () {
+ $instance.toggleClass('hide');
});
- var funABI = this.getConstructorInterface(abi);
- var $createInterface = $('
');
- var appendFunctions = function (address, $el) {
+ var $events = $('
');
- var $instance = $('
');
- if (self.options.removable_instances) {
- var $close = $('
');
- $close.click(function () { $instance.remove(); });
- $instance.append($close);
- }
- var context = self.options.vm ? 'memory' : 'blockchain';
- var $title = $('
').text(contract.name + ' at ' + (self.options.vm ? '0x' : '') + address.toString('hex') + ' (' + context + ')');
- $title.click(function () {
- $instance.toggleClass('hide');
- });
-
- var $events = $('
');
-
- var parseLogs = function (err, response) {
- if (err) {
- return;
- }
+ var parseLogs = function (err, response) {
+ if (err) {
+ return;
+ }
- var $event = $('
');
+ var $event = $('
');
- var $close = $('
');
- $close.click(function () { $event.remove(); });
+ var $close = $('
');
+ $close.click(function () { $event.remove(); });
- $event.append($('
').text(response.event))
- .append($('
').text(JSON.stringify(response.args, null, 2)))
- .append($close);
+ $event.append($('
').text(response.event))
+ .append($('
').text(JSON.stringify(response.args, null, 2)))
+ .append($close);
- $events.append($event);
- };
+ $events.append($event);
+ };
- if (self.options.vm) {
- // FIXME: support indexed events
- var eventABI = {};
+ if (self.options.vm) {
+ // FIXME: support indexed events
+ var eventABI = {};
- $.each(abi, function (i, funABI) {
- if (funABI.type !== 'event') {
- return;
- }
+ $.each(abi, function (i, funABI) {
+ if (funABI.type !== 'event') {
+ return;
+ }
var hash = ethJSABI.eventID(funABI.name, funABI.inputs.map(function (item) { return item.type; }));
eventABI[hash.toString('hex')] = { event: funABI.name, inputs: funABI.inputs };
+ });
+
+ self.vm.on('afterTx', function (response) {
+ for (var i in response.vm.logs) {
+ // [address, topics, mem]
+ var log = response.vm.logs[i];
+ var abi = eventABI[log[1][0].toString('hex')];
+ var event = abi.event;
+ var decoded;
+
+ try {
+ var types = abi.inputs.map(function (item) {
+ return item.type;
});
+ decoded = ethJSABI.rawDecode(types, log[2]);
+ decoded = ethJSABI.stringify(types, decoded);
+ } catch (e) {
+ decoded = '0x' + log[2].toString('hex');
+ }
- self.vm.on('afterTx', function (response) {
- for (var i in response.vm.logs) {
- // [address, topics, mem]
- var log = response.vm.logs[i];
- var abi = eventABI[log[1][0].toString('hex')];
- var event = abi.event;
- var decoded;
-
- try {
- var types = abi.inputs.map(function (item) {
- return item.type;
- });
- decoded = ethJSABI.rawDecode(types, log[2]);
- decoded = ethJSABI.stringify(types, decoded);
- } catch (e) {
- decoded = '0x' + log[2].toString('hex');
- }
-
- parseLogs(null, { event: event, args: decoded });
- }
- });
- } else {
- var eventFilter = this.web3.eth.contract(abi).at(address).allEvents();
- eventFilter.watch(parseLogs);
+ parseLogs(null, { event: event, args: decoded });
}
- $instance.append($title);
-
- // Add the fallback function
- $instance.append(self.getCallButton({
- abi: { constant: false, inputs: [], name: '(fallback)', outputs: [], type: 'function' },
- encode: function (args) {
- return '';
- },
- address: address
- }));
-
- $.each(abi, function (i, funABI) {
- if (funABI.type !== 'function') {
- return;
- }
- // @todo getData cannot be used with overloaded functions
- $instance.append(self.getCallButton({
- abi: funABI,
- encode: function (args) {
- var types = [];
- for (var i = 0; i < funABI.inputs.length; i++) {
- types.push(funABI.inputs[i].type);
- }
-
- return Buffer.concat([ ethJSABI.methodID(funABI.name, types), ethJSABI.rawEncode(types, args) ]).toString('hex');
- },
- address: address
- }));
- });
- ($el || $createInterface).append($instance.append($events));
- };
-
- if (!address || !$target) {
- $createInterface.append(this.getCallButton({
- abi: funABI,
- encode: function (args) {
- var types = [];
- for (var i = 0; i < funABI.inputs.length; i++) {
- types.push(funABI.inputs[i].type);
- }
-
- // NOTE: the caller will concatenate the bytecode and this
- // it could be done here too for consistency
- return ethJSABI.rawEncode(types, args).toString('hex');
- },
- contractName: contract.name,
- bytecode: contract.bytecode,
- appendFunctions: appendFunctions
- }));
+ });
} else {
- appendFunctions(address, $target);
+ var eventFilter = this.web3.eth.contract(abi).at(address).allEvents();
+ eventFilter.watch(parseLogs);
}
+ $instance.append($title);
+
+ // Add the fallback function
+ $instance.append(self.getCallButton({
+ abi: { constant: false, inputs: [], name: '(fallback)', outputs: [], type: 'function' },
+ encode: function (args) {
+ return '';
+ },
+ address: address
+ }));
+
+ $.each(abi, function (i, funABI) {
+ if (funABI.type !== 'function') {
+ return;
+ }
+ // @todo getData cannot be used with overloaded functions
+ $instance.append(self.getCallButton({
+ abi: funABI,
+ encode: function (args) {
+ var types = [];
+ for (var i = 0; i < funABI.inputs.length; i++) {
+ types.push(funABI.inputs[i].type);
+ }
+
+ return Buffer.concat([ ethJSABI.methodID(funABI.name, types), ethJSABI.rawEncode(types, args) ]).toString('hex');
+ },
+ address: address
+ }));
+ });
+ ($el || $createInterface).append($instance.append($events));
+ };
+
+ if (!address || !$target) {
+ $createInterface.append(this.getCallButton({
+ abi: funABI,
+ encode: function (args) {
+ var types = [];
+ for (var i = 0; i < funABI.inputs.length; i++) {
+ types.push(funABI.inputs[i].type);
+ }
- return $createInterface;
+ // NOTE: the caller will concatenate the bytecode and this
+ // it could be done here too for consistency
+ return ethJSABI.rawEncode(types, args).toString('hex');
+ },
+ contractName: contract.name,
+ bytecode: contract.bytecode,
+ appendFunctions: appendFunctions
+ }));
+ } else {
+ appendFunctions(address, $target);
+ }
+
+ return $createInterface;
};
UniversalDApp.prototype.getConstructorInterface = function (abi) {
- var funABI = { 'name': '', 'inputs': [], 'type': 'constructor', 'outputs': [] };
- for (var i = 0; i < abi.length; i++) {
- if (abi[i].type === 'constructor') {
- funABI.inputs = abi[i].inputs || [];
- break;
- }
+ var funABI = { 'name': '', 'inputs': [], 'type': 'constructor', 'outputs': [] };
+ for (var i = 0; i < abi.length; i++) {
+ if (abi[i].type === 'constructor') {
+ funABI.inputs = abi[i].inputs || [];
+ break;
}
- return funABI;
+ }
+ return funABI;
};
UniversalDApp.prototype.getCallButton = function (args) {
- var self = this;
- // args.abi, args.encode, args.bytecode [constr only], args.address [fun only]
- // args.contractName [constr only], args.appendFunctions [constr only]
- var isConstructor = args.bytecode !== undefined;
- var lookupOnly = (args.abi.constant && !isConstructor);
-
- var inputs = '';
- $.each(args.abi.inputs, function (i, inp) {
- if (inputs !== '') {
- inputs += ', ';
- }
- inputs += inp.type + ' ' + inp.name;
- });
- var inputField = $('
').attr('placeholder', inputs).attr('title', inputs);
- var $outputOverride = $('
');
- var outputSpan = $('
');
-
- var getReturnOutput = function (result) {
- var returnName = lookupOnly ? 'Value' : 'Result';
- var returnCls = lookupOnly ? 'value' : 'returned';
- return $('
').html('
' + returnName + ': ' + JSON.stringify(result, null, 2));
- };
+ var self = this;
+ // args.abi, args.encode, args.bytecode [constr only], args.address [fun only]
+ // args.contractName [constr only], args.appendFunctions [constr only]
+ var isConstructor = args.bytecode !== undefined;
+ var lookupOnly = (args.abi.constant && !isConstructor);
+
+ var inputs = '';
+ $.each(args.abi.inputs, function (i, inp) {
+ if (inputs !== '') {
+ inputs += ', ';
+ }
+ inputs += inp.type + ' ' + inp.name;
+ });
+ var inputField = $('
').attr('placeholder', inputs).attr('title', inputs);
+ var $outputOverride = $('
');
+ var outputSpan = $('
');
+
+ var getReturnOutput = function (result) {
+ var returnName = lookupOnly ? 'Value' : 'Result';
+ var returnCls = lookupOnly ? 'value' : 'returned';
+ return $('
').html('
' + returnName + ': ' + JSON.stringify(result, null, 2));
+ };
+
+ var getGasUsedOutput = function (result, vmResult) {
+ var $gasUsed = $('
');
+ var caveat = lookupOnly ? '
(caveat)' : '';
+ var gas;
+ if (result.gasUsed) {
+ gas = result.gasUsed.toString(10);
+ $gasUsed.html('
Transaction cost: ' + gas + ' gas. ' + caveat);
+ }
+ if (vmResult.gasUsed) {
+ var $callGasUsed = $('
');
+ gas = vmResult.gasUsed.toString(10);
+ $callGasUsed.append('
Execution cost: ' + gas + ' gas.');
+ $gasUsed.append($callGasUsed);
+ }
+ return $gasUsed;
+ };
+
+ var getDecodedOutput = function (result) {
+ var $decoded;
+ if (Array.isArray(result)) {
+ $decoded = $('
');
+ for (var i = 0; i < result.length; i++) {
+ $decoded.append($('- ').text(result[i]));
+ }
+ } else {
+ $decoded = result;
+ }
+ return $('
').html('
Decoded: ').append($decoded);
+ };
+
+ var getOutput = function () {
+ var $result = $('
');
+ var $close = $('
');
+ $close.click(function () { $result.remove(); });
+ $result.append($close);
+ return $result;
+ };
+ var clearOutput = function ($result) {
+ $(':not(.udapp-close)', $result).remove();
+ };
+ var replaceOutput = function ($result, message) {
+ clearOutput($result);
+ $result.append(message);
+ };
+
+ var handleCallButtonClick = function (ev, $result) {
+ if (!$result) {
+ $result = getOutput();
+ if (lookupOnly && !inputs.length) {
+ $outputOverride.empty().append($result);
+ } else {
+ outputSpan.append($result);
+ }
+ }
- var getGasUsedOutput = function (result, vmResult) {
- var $gasUsed = $('
');
- var caveat = lookupOnly ? '
(caveat)' : '';
- if (result.gasUsed) {
- var gas = result.gasUsed.toString(10);
- $gasUsed.html('
Transaction cost: ' + gas + ' gas. ' + caveat);
- }
- if (vmResult.gasUsed) {
- var $callGasUsed = $('
');
- var gas = vmResult.gasUsed.toString(10);
- $callGasUsed.append('
Execution cost: ' + gas + ' gas.');
- $gasUsed.append($callGasUsed);
- }
- return $gasUsed;
- };
+ var funArgs = '';
+ try {
+ funArgs = $.parseJSON('[' + inputField.val() + ']');
+ } catch (e) {
+ replaceOutput($result, $('
').text('Error encoding arguments: ' + e));
+ return;
+ }
+ var data = '';
+ if (!isConstructor || funArgs.length > 0) {
+ try {
+ data = args.encode(funArgs);
+ } catch (e) {
+ replaceOutput($result, $('
').text('Error encoding arguments: ' + e));
+ return;
+ }
+ }
+ if (data.slice(0, 9) === 'undefined') {
+ data = data.slice(9);
+ }
+ if (data.slice(0, 2) === '0x') {
+ data = data.slice(2);
+ }
- var getDecodedOutput = function (result) {
- var $decoded;
- if (Array.isArray(result)) {
- $decoded = $('
');
- for (var i = 0; i < result.length; i++) {
- $decoded.append($('- ').text(result[i]));
- }
- } else {
- $decoded = result;
- }
- return $('
').html('
Decoded: ').append($decoded);
- };
+ replaceOutput($result, $('
Waiting for transaction to be mined...'));
+
+ if (isConstructor) {
+ if (args.bytecode.indexOf('_') >= 0) {
+ replaceOutput($result, $('
Deploying and linking required libraries...'));
+ self.linkBytecode(args.contractName, function (err, bytecode) {
+ if (err) {
+ replaceOutput($result, $('
').text('Error deploying required libraries: ' + err));
+ } else {
+ args.bytecode = bytecode;
+ handleCallButtonClick(ev, $result);
+ }
+ });
+ return;
+ } else {
+ data = args.bytecode + data;
+ }
+ }
- var getOutput = function () {
- var $result = $('
');
- var $close = $('
');
- $close.click(function () { $result.remove(); });
- $result.append($close);
- return $result;
- };
- var clearOutput = function ($result) {
- $(':not(.udapp-close)', $result).remove();
- };
- var replaceOutput = function ($result, message) {
+ self.runTx(data, args, function (err, result) {
+ if (err) {
+ replaceOutput($result, $('
').text(err).addClass('error'));
+ } else if (self.options.vm && result.vm.exception && result.vm.exceptionError) {
+ replaceOutput($result, $('
').text('VM Exception: ' + result.vm.exceptionError).addClass('error'));
+ } else if (self.options.vm && result.vm.return === undefined) {
+ replaceOutput($result, $('
').text('Exception during execution.').addClass('error'));
+ } else if (self.options.vm && isConstructor) {
+ replaceOutput($result, getGasUsedOutput(result, result.vm));
+ args.appendFunctions(result.createdAddress);
+ } else if (self.options.vm) {
+ var outputObj = '0x' + result.vm.return.toString('hex');
clearOutput($result);
- $result.append(message);
- };
+ $result.append(getReturnOutput(outputObj)).append(getGasUsedOutput(result, result.vm));
- var handleCallButtonClick = function (ev, $result) {
- if (!$result) {
- $result = getOutput();
- if (lookupOnly && !inputs.length) {
- $outputOverride.empty().append($result);
- } else {
- outputSpan.append($result);
+ // Only decode if there supposed to be fields
+ if (args.abi.outputs.length > 0) {
+ try {
+ var i;
+
+ var outputTypes = [];
+ for (i = 0; i < args.abi.outputs.length; i++) {
+ outputTypes.push(args.abi.outputs[i].type);
}
- }
- var funArgs = '';
- try {
- funArgs = $.parseJSON('[' + inputField.val() + ']');
- } catch (e) {
- replaceOutput($result, $('
').text('Error encoding arguments: ' + e));
- return;
- }
- var data = '';
- if (!isConstructor || funArgs.length > 0) {
- try {
- data = args.encode(funArgs);
- } catch (e) {
- replaceOutput($result, $('
').text('Error encoding arguments: ' + e));
- return;
+ // decode data
+ var decodedObj = ethJSABI.rawDecode(outputTypes, result.vm.return);
+
+ // format decoded data
+ decodedObj = ethJSABI.stringify(outputTypes, decodedObj);
+ for (i = 0; i < outputTypes.length; i++) {
+ var name = args.abi.outputs[i].name;
+ if (name.length > 0) {
+ decodedObj[i] = outputTypes[i] + ' ' + name + ': ' + decodedObj[i];
+ } else {
+ decodedObj[i] = outputTypes[i] + ': ' + decodedObj[i];
+ }
}
- }
- if (data.slice(0, 9) === 'undefined') {
- data = data.slice(9);
- }
- if (data.slice(0, 2) === '0x') {
- data = data.slice(2);
- }
- replaceOutput($result, $('
Waiting for transaction to be mined...'));
-
- if (isConstructor) {
- if (args.bytecode.indexOf('_') >= 0) {
- replaceOutput($result, $('
Deploying and linking required libraries...'));
- self.linkBytecode(args.contractName, function (err, bytecode) {
- if (err) {
- replaceOutput($result, $('
').text('Error deploying required libraries: ' + err));
- } else {
- args.bytecode = bytecode;
- handleCallButtonClick(ev, $result);
- }
- });
- return;
- } else {
- data = args.bytecode + data;
- }
+ $result.append(getDecodedOutput(decodedObj));
+ } catch (e) {
+ $result.append(getDecodedOutput('Failed to decode output: ' + e));
+ }
}
-
- self.runTx(data, args, function (err, result) {
- if (err) {
- replaceOutput($result, $('
').text(err).addClass('error'));
- } else if (self.options.vm && result.vm.exception && result.vm.exceptionError) {
- replaceOutput($result, $('
').text('VM Exception: ' + result.vm.exceptionError).addClass('error'));
- } else if (self.options.vm && result.vm.return === undefined) {
- replaceOutput($result, $('
').text('Exception during execution.').addClass('error'));
- } else if (self.options.vm && isConstructor) {
- replaceOutput($result, getGasUsedOutput(result, result.vm));
- args.appendFunctions(result.createdAddress);
- } else if (self.options.vm) {
- var outputObj = '0x' + result.vm.return.toString('hex');
- clearOutput($result);
- $result.append(getReturnOutput(outputObj)).append(getGasUsedOutput(result, result.vm));
-
- // Only decode if there supposed to be fields
- if (args.abi.outputs.length > 0) {
- try {
- var outputTypes = [];
- for (var i = 0; i < args.abi.outputs.length; i++) {
- outputTypes.push(args.abi.outputs[i].type);
- }
-
- // decode data
- var decodedObj = ethJSABI.rawDecode(outputTypes, result.vm.return);
-
- // format decoded data
- decodedObj = ethJSABI.stringify(outputTypes, decodedObj);
- for (var i = 0; i < outputTypes.length; i++) {
- var name = args.abi.outputs[i].name;
- if (name.length > 0) {
- decodedObj[i] = outputTypes[i] + ' ' + name + ': ' + decodedObj[i];
- } else {
- decodedObj[i] = outputTypes[i] + ': ' + decodedObj[i];
- }
- }
-
- $result.append(getDecodedOutput(decodedObj));
- } catch (e) {
- $result.append(getDecodedOutput('Failed to decode output: ' + e));
- }
- }
- } else if (args.abi.constant && !isConstructor) {
- replaceOutput($result, getReturnOutput(result));
- } else {
- tryTillResponse(self.web3, result, function (err, result) {
- if (err) {
- replaceOutput($result, $('
').text(err).addClass('error'));
- } else if (isConstructor) {
- $result.html('');
- args.appendFunctions(result.contractAddress);
- } else {
- clearOutput($result);
- $result.append(getReturnOutput(result)).append(getGasUsedOutput(result));
- }
- });
-
- }
+ } else if (args.abi.constant && !isConstructor) {
+ replaceOutput($result, getReturnOutput(result));
+ } else {
+ tryTillResponse(self.web3, result, function (err, result) {
+ if (err) {
+ replaceOutput($result, $('
').text(err).addClass('error'));
+ } else if (isConstructor) {
+ $result.html('');
+ args.appendFunctions(result.contractAddress);
+ } else {
+ clearOutput($result);
+ $result.append(getReturnOutput(result)).append(getGasUsedOutput(result));
+ }
});
- };
-
- var button = $('
')
- .addClass('call')
- .attr('title', args.abi.name)
- .text(args.bytecode ? 'Create' : args.abi.name)
- .click(handleCallButtonClick);
-
- if (lookupOnly && !inputs.length) {
- handleCallButtonClick();
- }
-
- var $contractProperty = $('
');
- $contractProperty
- .toggleClass('constant', !isConstructor && args.abi.constant)
- .toggleClass('hasArgs', args.abi.inputs.length > 0)
- .toggleClass('constructor', isConstructor)
- .append(button)
- .append((lookupOnly && !inputs.length) ? $outputOverride : inputField);
- return $contractProperty.append(outputSpan);
+ }
+ });
+ };
+
+ var button = $('
')
+ .addClass('call')
+ .attr('title', args.abi.name)
+ .text(args.bytecode ? 'Create' : args.abi.name)
+ .click(handleCallButtonClick);
+
+ if (lookupOnly && !inputs.length) {
+ handleCallButtonClick();
+ }
+
+ var $contractProperty = $('
');
+ $contractProperty
+ .toggleClass('constant', !isConstructor && args.abi.constant)
+ .toggleClass('hasArgs', args.abi.inputs.length > 0)
+ .toggleClass('constructor', isConstructor)
+ .append(button)
+ .append((lookupOnly && !inputs.length) ? $outputOverride : inputField);
+ return $contractProperty.append(outputSpan);
};
UniversalDApp.prototype.linkBytecode = function (contractName, cb) {
- var bytecode = this.getContractByName(contractName).bytecode;
- if (bytecode.indexOf('_') < 0) {
- return cb(null, bytecode);
+ var bytecode = this.getContractByName(contractName).bytecode;
+ if (bytecode.indexOf('_') < 0) {
+ return cb(null, bytecode);
+ }
+ var m = bytecode.match(/__([^_]{1,36})__/);
+ if (!m) {
+ return cb('Invalid bytecode format.');
+ }
+ var libraryName = m[1];
+ if (!this.getContractByName(libraryName)) {
+ return cb('Library ' + libraryName + ' not found.');
+ }
+ var self = this;
+ this.deployLibrary(libraryName, function (err, address) {
+ if (err) {
+ return cb(err);
}
- var m = bytecode.match(/__([^_]{1,36})__/);
- if (!m) {
- return cb('Invalid bytecode format.');
+ var libLabel = '__' + libraryName + Array(39 - libraryName.length).join('_');
+ var hexAddress = address.toString('hex');
+ if (hexAddress.slice(0, 2) === '0x') {
+ hexAddress = hexAddress.slice(2);
}
- var libraryName = m[1];
- if (!this.getContractByName(libraryName)) {
- return cb('Library ' + libraryName + ' not found.');
+ hexAddress = Array(40 - hexAddress.length + 1).join('0') + hexAddress;
+ while (bytecode.indexOf(libLabel) >= 0) {
+ bytecode = bytecode.replace(libLabel, hexAddress);
}
- var self = this;
- this.deployLibrary(libraryName, function (err, address) {
- if (err) {
- return cb(err);
- }
- var libLabel = '__' + libraryName + Array(39 - libraryName.length).join('_');
- var hexAddress = address.toString('hex');
- if (hexAddress.slice(0, 2) === '0x') {
- hexAddress = hexAddress.slice(2);
- }
- hexAddress = Array(40 - hexAddress.length + 1).join('0') + hexAddress;
- while (bytecode.indexOf(libLabel) >= 0) {
- bytecode = bytecode.replace(libLabel, hexAddress);
- }
- self.getContractByName(contractName).bytecode = bytecode;
- self.linkBytecode(contractName, cb);
- });
+ self.getContractByName(contractName).bytecode = bytecode;
+ self.linkBytecode(contractName, cb);
+ });
};
UniversalDApp.prototype.deployLibrary = function (contractName, cb) {
- if (this.getContractByName(contractName).address) {
- return cb(null, this.getContractByName(contractName).address);
- }
- var self = this;
- var bytecode = this.getContractByName(contractName).bytecode;
- if (bytecode.indexOf('_') >= 0) {
- this.linkBytecode(contractName, function (err, bytecode) {
- if (err) cb(err);
- else self.deployLibrary(contractName, cb);
- });
- } else {
- this.runTx(bytecode, { abi: { constant: false }, bytecode: bytecode }, function (err, result) {
- if (err) {
- return cb(err);
- }
- if (self.options.vm) {
- self.getContractByName(contractName).address = result.createdAddress;
- cb(err, result.createdAddress);
- } else {
- tryTillResponse(self.web3, result, function(err, finalResult) {
- if (err) return cb(err);
- self.getContractByName(contractName).address = finalResult.contractAddress;
- cb(null, finalResult.contractAddress);
- });
- }
+ if (this.getContractByName(contractName).address) {
+ return cb(null, this.getContractByName(contractName).address);
+ }
+ var self = this;
+ var bytecode = this.getContractByName(contractName).bytecode;
+ if (bytecode.indexOf('_') >= 0) {
+ this.linkBytecode(contractName, function (err, bytecode) {
+ if (err) cb(err);
+ else self.deployLibrary(contractName, cb);
+ });
+ } else {
+ this.runTx(bytecode, { abi: { constant: false }, bytecode: bytecode }, function (err, result) {
+ if (err) {
+ return cb(err);
+ }
+ if (self.options.vm) {
+ self.getContractByName(contractName).address = result.createdAddress;
+ cb(err, result.createdAddress);
+ } else {
+ tryTillResponse(self.web3, result, function (err, finalResult) {
+ if (err) return cb(err);
+ self.getContractByName(contractName).address = finalResult.contractAddress;
+ cb(null, finalResult.contractAddress);
});
- }
+ }
+ });
+ }
};
UniversalDApp.prototype.clickContractAt = function (self, $output, contract) {
- var address = prompt('What Address is this contract at in the Blockchain? ie: 0xdeadbeaf...');
- self.getInstanceInterface(contract, address, $output);
+ var address = prompt('What Address is this contract at in the Blockchain? ie: 0xdeadbeaf...');
+ self.getInstanceInterface(contract, address, $output);
};
UniversalDApp.prototype.runTx = function (data, args, cb) {
- var self = this;
- var to = args.address;
- var constant = args.abi.constant;
- var isConstructor = args.bytecode !== undefined;
- if (data.slice(0, 2) !== '0x') {
- data = '0x' + data;
+ var self = this;
+ var to = args.address;
+ var constant = args.abi.constant;
+ var isConstructor = args.bytecode !== undefined;
+ if (data.slice(0, 2) !== '0x') {
+ data = '0x' + data;
+ }
+
+ var gas = self.options.getGas ? self.options.getGas : 1000000;
+
+ var value = 0;
+ if (self.options.getValue) {
+ try {
+ value = self.options.getValue();
+ } catch (e) {
+ return cb(e);
}
-
- var gas = self.options.getGas ? self.options.getGas : 1000000;
-
- var value = 0;
- if (self.options.getValue) {
- try {
- value = self.options.getValue();
- } catch (e) {
- return cb(e);
- }
- }
-
- var tx;
- if (!this.vm) {
- tx = {
- from: self.options.getAddress ? self.options.getAddress() : this.web3.eth.accounts[0],
- to: to,
- data: data,
- gas: gas,
- value: value
- };
- if (constant && !isConstructor) {
- this.web3.eth.call(tx, cb);
- } else {
- this.web3.eth.estimateGas(tx, function (err, resp) {
- tx.gas = resp;
- if (!err) {
- self.web3.eth.sendTransaction(tx, cb);
- } else {
- cb(err, resp);
- }
- });
- }
+ }
+
+ var tx;
+ if (!this.vm) {
+ tx = {
+ from: self.options.getAddress ? self.options.getAddress() : this.web3.eth.accounts[0],
+ to: to,
+ data: data,
+ gas: gas,
+ value: value
+ };
+ if (constant && !isConstructor) {
+ this.web3.eth.call(tx, cb);
} else {
- try {
- var address = this.options.getAddress ? this.options.getAddress() : this.getAccounts()[0];
- var account = this.accounts[address];
- tx = new EthJSTX({
- nonce: new Buffer([account.nonce++]), //@todo count beyond 255
- gasPrice: 1,
- gasLimit: 3000000000, // plenty
- to: to,
- value: new BN(value, 10),
- data: new Buffer(data.slice(2), 'hex')
- });
- tx.sign(account.privateKey);
- var block = new EthJSBlock({
- header: {
- // FIXME: support coinbase, difficulty, number and gasLimit
- timestamp: new Date().getTime() / 1000 | 0
- },
- transactions: [],
- uncleHeaders: []
- });
- this.vm.runTx({block: block, tx: tx, skipBalance: true, skipNonce: true}, cb);
- } catch (e) {
- cb(e, null);
+ this.web3.eth.estimateGas(tx, function (err, resp) {
+ tx.gas = resp;
+ if (!err) {
+ self.web3.eth.sendTransaction(tx, cb);
+ } else {
+ cb(err, resp);
}
+ });
}
+ } else {
+ try {
+ var address = this.options.getAddress ? this.options.getAddress() : this.getAccounts()[0];
+ var account = this.accounts[address];
+ tx = new EthJSTX({
+ nonce: new Buffer([account.nonce++]), // @todo count beyond 255
+ gasPrice: 1,
+ gasLimit: 3000000000, // plenty
+ to: to,
+ value: new BN(value, 10),
+ data: new Buffer(data.slice(2), 'hex')
+ });
+ tx.sign(account.privateKey);
+ var block = new EthJSBlock({
+ header: {
+ // FIXME: support coinbase, difficulty, number and gasLimit
+ timestamp: new Date().getTime() / 1000 | 0
+ },
+ transactions: [],
+ uncleHeaders: []
+ });
+ this.vm.runTx({block: block, tx: tx, skipBalance: true, skipNonce: true}, cb);
+ } catch (e) {
+ cb(e, null);
+ }
+ }
};
function tryTillResponse (web3, txhash, done) {
- web3.eth.getTransactionReceipt(txhash, testResult);
+ web3.eth.getTransactionReceipt(txhash, testResult);
- function testResult (err, address) {
- if (!err && !address) {
- setTimeout(function () { tryTillResponse(web3, txhash, done); }, 500);
- } else {
- done(err, address);
- }
+ function testResult (err, address) {
+ if (!err && !address) {
+ setTimeout(function () { tryTillResponse(web3, txhash, done); }, 500);
+ } else {
+ done(err, address);
}
-
+ }
}
module.exports = UniversalDApp;