Commit 2126391e authored by Alex Beregszaszi's avatar Alex Beregszaszi

Format using standard

parent 8914952e
/* global chrome */ /* global chrome */
'use strict'; 'use strict'
chrome.browserAction.onClicked.addListener(function (tab) { chrome.browserAction.onClicked.addListener(function (tab) {
chrome.storage.sync.set({ 'chrome-app-sync': true }); chrome.storage.sync.set({ 'chrome-app-sync': true })
chrome.tabs.create({ 'url': chrome.extension.getURL('index.html') }, function (tab) { chrome.tabs.create({ 'url': chrome.extension.getURL('index.html') }, function (tab) {
// tab opened // tab opened
}); })
}); })
var fs = require('fs'); var fs = require('fs')
var solc = require('solc/wrapper'); var solc = require('solc/wrapper')
var soljson = require('../soljson'); var soljson = require('../soljson')
var compiler = solc(soljson); var compiler = solc(soljson)
var inputs = require('../test-browser/mockcompiler/requests.js'); var inputs = require('../test-browser/mockcompiler/requests.js')
var compilationResult = gatherCompilationResults(inputs); var compilationResult = gatherCompilationResults(inputs)
replaceSolCompiler(compilationResult); replaceSolCompiler(compilationResult)
function gatherCompilationResults (sol) { function gatherCompilationResults (sol) {
var compilationResult = {}; var compilationResult = {}
for (var k in sol) { for (var k in sol) {
var item = sol[k]; var item = sol[k]
var result = compile(item, 1); var result = compile(item, 1)
compilationResult[result.key] = result; compilationResult[result.key] = result
result = compile(item, 0); result = compile(item, 0)
compilationResult[result.key] = result; compilationResult[result.key] = result
} }
return compilationResult; return compilationResult
} }
function compile (source, optimization) { function compile (source, optimization) {
var missingInputs = []; var missingInputs = []
var result = compiler.compile(source, optimization, function (path) { var result = compiler.compile(source, optimization, function (path) {
missingInputs.push(path); missingInputs.push(path)
}); })
var key = optimization.toString(); var key = optimization.toString()
for (var k in source.sources) { for (var k in source.sources) {
key += k + source.sources[k]; key += k + source.sources[k]
} }
key = key.replace(/(\t)|(\n)|( )/g, ''); key = key.replace(/(\t)|(\n)|( )/g, '')
return { return {
key: key, key: key,
source: source, source: source,
optimization: optimization, optimization: optimization,
missingInputs: missingInputs, missingInputs: missingInputs,
result: result result: result
}; }
} }
function replaceSolCompiler (results) { function replaceSolCompiler (results) {
fs.readFile('./test-browser/mockcompiler/compiler.js', 'utf8', function (error, data) { fs.readFile('./test-browser/mockcompiler/compiler.js', 'utf8', function (error, data) {
if (error) { if (error) {
console.log(error); console.log(error)
process.exit(1); process.exit(1)
return; return
} }
data = data + '\n\nvar mockData = ' + JSON.stringify(results) + ';\n'; data = data + '\n\nvar mockData = ' + JSON.stringify(results) + ';\n'
fs.writeFile('./soljson.js', data, 'utf8', function (error) { fs.writeFile('./soljson.js', data, 'utf8', function (error) {
if (error) { if (error) {
console.log(error); console.log(error)
process.exit(1); process.exit(1)
return; return
} }
}); })
}); })
} }
/* global alert, confirm, prompt, Option, Worker */ /* global alert, confirm, prompt, Option, Worker */
'use strict'; 'use strict'
var $ = require('jquery'); var $ = require('jquery')
var base64 = require('js-base64').Base64; var base64 = require('js-base64').Base64
var utils = require('./app/utils'); var utils = require('./app/utils')
var QueryParams = require('./app/query-params'); var QueryParams = require('./app/query-params')
var queryParams = new QueryParams(); var queryParams = new QueryParams()
var GistHandler = require('./app/gist-handler'); var GistHandler = require('./app/gist-handler')
var gistHandler = new GistHandler(); var gistHandler = new GistHandler()
var Storage = require('./app/storage'); var Storage = require('./app/storage')
var Editor = require('./app/editor'); var Editor = require('./app/editor')
var Renderer = require('./app/renderer'); var Renderer = require('./app/renderer')
var Compiler = require('./app/compiler'); var Compiler = require('./app/compiler')
var ExecutionContext = require('./app/execution-context'); var ExecutionContext = require('./app/execution-context')
var UniversalDApp = require('./universal-dapp.js'); var UniversalDApp = require('./universal-dapp.js')
var Debugger = require('./app/debugger'); var Debugger = require('./app/debugger')
var FormalVerification = require('./app/formalVerification'); var FormalVerification = require('./app/formalVerification')
var EventManager = require('./lib/eventManager'); var EventManager = require('./lib/eventManager')
// The event listener needs to be registered as early as possible, because the // The event listener needs to be registered as early as possible, because the
// parent will send the message upon the "load" event. // parent will send the message upon the "load" event.
var filesToLoad = null; var filesToLoad = null
var loadFilesCallback = function (files) { filesToLoad = files; }; // will be replaced later var loadFilesCallback = function (files) { filesToLoad = files } // will be replaced later
window.addEventListener('message', function (ev) { window.addEventListener('message', function (ev) {
if (typeof ev.data === typeof [] && ev.data[0] === 'loadFiles') { if (typeof ev.data === typeof [] && ev.data[0] === 'loadFiles') {
loadFilesCallback(ev.data[1]); loadFilesCallback(ev.data[1])
} }
}, false); }, false)
/* /*
trigger tabChanged trigger tabChanged
*/ */
var run = function () { var run = function () {
var self = this; var self = this
this.event = new EventManager(); this.event = new EventManager()
var storage = new Storage(updateFiles); var storage = new Storage(updateFiles)
function loadFiles (files) { function loadFiles (files) {
for (var f in files) { for (var f in files) {
var key = utils.fileKey(f); var key = utils.fileKey(f)
var content = files[f].content; var content = files[f].content
storage.loadFile(key, content); storage.loadFile(key, content)
} }
editor.setCacheFile(utils.fileKey(Object.keys(files)[0])); editor.setCacheFile(utils.fileKey(Object.keys(files)[0]))
updateFiles(); updateFiles()
} }
loadFilesCallback = function (files) { loadFilesCallback = function (files) {
loadFiles(files); loadFiles(files)
}; }
if (filesToLoad !== null) { if (filesToLoad !== null) {
loadFiles(filesToLoad); loadFiles(filesToLoad)
} }
// -------- check file upload capabilities ------- // -------- check file upload capabilities -------
if (!(window.File || window.FileReader || window.FileList || window.Blob)) { if (!(window.File || window.FileReader || window.FileList || window.Blob)) {
$('.uploadFile').remove(); $('.uploadFile').remove()
} }
// ------------------ gist load ---------------- // ------------------ gist load ----------------
...@@ -70,47 +73,47 @@ var run = function () { ...@@ -70,47 +73,47 @@ var run = function () {
success: function (response) { success: function (response) {
if (response.data) { if (response.data) {
if (!response.data.files) { if (!response.data.files) {
alert('Gist load error: ' + response.data.message); alert('Gist load error: ' + response.data.message)
return; return
} }
loadFiles(response.data.files); loadFiles(response.data.files)
} }
} }
}); })
}); })
// ----------------- storage sync -------------------- // ----------------- storage sync --------------------
window.syncStorage = storage.sync; window.syncStorage = storage.sync
storage.sync(); storage.sync()
// ----------------- editor ---------------------- // ----------------- editor ----------------------
var editor = new Editor(loadingFromGist, storage); var editor = new Editor(loadingFromGist, storage)
// ----------------- tabbed menu ------------------- // ----------------- tabbed menu -------------------
$('#options li').click(function (ev) { $('#options li').click(function (ev) {
var $el = $(this); var $el = $(this)
selectTab($el); selectTab($el)
}); })
var selectTab = function (el) { var selectTab = function (el) {
var match = /[a-z]+View/.exec(el.get(0).className); var match = /[a-z]+View/.exec(el.get(0).className)
if (!match) return; if (!match) return
var cls = match[0]; var cls = match[0]
if (!el.hasClass('active')) { if (!el.hasClass('active')) {
el.parent().find('li').removeClass('active'); el.parent().find('li').removeClass('active')
$('#optionViews').attr('class', '').addClass(cls); $('#optionViews').attr('class', '').addClass(cls)
el.addClass('active'); el.addClass('active')
}
self.event.trigger('tabChanged', [cls])
} }
self.event.trigger('tabChanged', [cls]);
};
// ------------------ gist publish -------------- // ------------------ gist publish --------------
$('#gist').click(function () { $('#gist').click(function () {
if (confirm('Are you sure you want to publish all your files anonymously as a public gist on github.com?')) { if (confirm('Are you sure you want to publish all your files anonymously as a public gist on github.com?')) {
var files = editor.packageFiles(); var files = editor.packageFiles()
var description = 'Created using browser-solidity: Realtime Ethereum Contract Compiler and Runtime. \n Load this file by pasting this gists URL or ID at https://ethereum.github.io/browser-solidity/#version=' + queryParams.get().version + '&optimize=' + queryParams.get().optimize + '&gist='; var description = 'Created using browser-solidity: Realtime Ethereum Contract Compiler and Runtime. \n Load this file by pasting this gists URL or ID at https://ethereum.github.io/browser-solidity/#version=' + queryParams.get().version + '&optimize=' + queryParams.get().optimize + '&gist='
$.ajax({ $.ajax({
url: 'https://api.github.com/gists', url: 'https://api.github.com/gists',
...@@ -122,159 +125,161 @@ var run = function () { ...@@ -122,159 +125,161 @@ var run = function () {
}) })
}).done(function (response) { }).done(function (response) {
if (response.html_url && confirm('Created a gist at ' + response.html_url + ' Would you like to open it in a new window?')) { if (response.html_url && confirm('Created a gist at ' + response.html_url + ' Would you like to open it in a new window?')) {
window.open(response.html_url, '_blank'); window.open(response.html_url, '_blank')
} }
}); })
} }
}); })
$('#copyOver').click(function () { $('#copyOver').click(function () {
var target = prompt( var target = prompt(
'To which other browser-solidity instance do you want to copy over all files?', 'To which other browser-solidity instance do you want to copy over all files?',
'https://ethereum.github.io/browser-solidity/' 'https://ethereum.github.io/browser-solidity/'
); )
if (target === null) { if (target === null) {
return; return
} }
var files = editor.packageFiles(); var files = editor.packageFiles()
$('<iframe/>', {src: target, style: 'display:none;', load: function () { $('<iframe/>', {
this.contentWindow.postMessage(['loadFiles', files], '*'); src: target,
}}).appendTo('body'); style: 'display:none;',
}); load: function () { this.contentWindow.postMessage(['loadFiles', files], '*') }
}).appendTo('body')
})
// ----------------- file selector------------- // ----------------- file selector-------------
var $filesEl = $('#files'); var $filesEl = $('#files')
var FILE_SCROLL_DELTA = 300; var FILE_SCROLL_DELTA = 300
$('.newFile').on('click', function () { $('.newFile').on('click', function () {
editor.newFile(); editor.newFile()
updateFiles(); updateFiles()
$filesEl.animate({ left: Math.max((0 - activeFilePos() + (FILE_SCROLL_DELTA / 2)), 0) + 'px' }, 'slow', function () { $filesEl.animate({ left: Math.max((0 - activeFilePos() + (FILE_SCROLL_DELTA / 2)), 0) + 'px' }, 'slow', function () {
reAdjust(); reAdjust()
}); })
}); })
// ----------------- file upload ------------- // ----------------- file upload -------------
$('.inputFile').on('change', function () { $('.inputFile').on('change', function () {
var fileList = $('input.inputFile')[0].files; var fileList = $('input.inputFile')[0].files
for (var i = 0; i < fileList.length; i++) { for (var i = 0; i < fileList.length; i++) {
var name = fileList[i].name; var name = fileList[i].name
if (!storage.exists(utils.fileKey(name)) || confirm('The file ' + name + ' already exists! Would you like to overwrite it?')) { if (!storage.exists(utils.fileKey(name)) || confirm('The file ' + name + ' already exists! Would you like to overwrite it?')) {
editor.uploadFile(fileList[i], updateFiles); editor.uploadFile(fileList[i], updateFiles)
} }
} }
$filesEl.animate({ left: Math.max((0 - activeFilePos() + (FILE_SCROLL_DELTA / 2)), 0) + 'px' }, 'slow', function () { $filesEl.animate({ left: Math.max((0 - activeFilePos() + (FILE_SCROLL_DELTA / 2)), 0) + 'px' }, 'slow', function () {
reAdjust(); reAdjust()
}); })
}); })
$filesEl.on('click', '.file:not(.active)', showFileHandler); $filesEl.on('click', '.file:not(.active)', showFileHandler)
$filesEl.on('click', '.file.active', function (ev) { $filesEl.on('click', '.file.active', function (ev) {
var $fileTabEl = $(this); var $fileTabEl = $(this)
var originalName = $fileTabEl.find('.name').text(); var originalName = $fileTabEl.find('.name').text()
ev.preventDefault(); ev.preventDefault()
if ($(this).find('input').length > 0) return false; if ($(this).find('input').length > 0) return false
var $fileNameInputEl = $('<input value="' + originalName + '"/>'); var $fileNameInputEl = $('<input value="' + originalName + '"/>')
$fileTabEl.html($fileNameInputEl); $fileTabEl.html($fileNameInputEl)
$fileNameInputEl.focus(); $fileNameInputEl.focus()
$fileNameInputEl.select(); $fileNameInputEl.select()
$fileNameInputEl.on('blur', handleRename); $fileNameInputEl.on('blur', handleRename)
$fileNameInputEl.keyup(handleRename); $fileNameInputEl.keyup(handleRename)
function handleRename (ev) { function handleRename (ev) {
ev.preventDefault(); ev.preventDefault()
if (ev.which && ev.which !== 13) return false; if (ev.which && ev.which !== 13) return false
var newName = ev.target.value; var newName = ev.target.value
$fileNameInputEl.off('blur'); $fileNameInputEl.off('blur')
$fileNameInputEl.off('keyup'); $fileNameInputEl.off('keyup')
if (newName !== originalName && confirm( if (newName !== originalName && confirm(
storage.exists(utils.fileKey(newName)) storage.exists(utils.fileKey(newName))
? 'Are you sure you want to overwrite: ' + newName + ' with ' + originalName + '?' ? 'Are you sure you want to overwrite: ' + newName + ' with ' + originalName + '?'
: 'Are you sure you want to rename: ' + originalName + ' to ' + newName + '?')) { : 'Are you sure you want to rename: ' + originalName + ' to ' + newName + '?')) {
storage.rename(utils.fileKey(originalName), utils.fileKey(newName)); storage.rename(utils.fileKey(originalName), utils.fileKey(newName))
editor.renameSession(utils.fileKey(originalName), utils.fileKey(newName)); editor.renameSession(utils.fileKey(originalName), utils.fileKey(newName))
editor.setCacheFile(utils.fileKey(newName)); editor.setCacheFile(utils.fileKey(newName))
} }
updateFiles(); updateFiles()
return false; return false
} }
return false; return false
}); })
$filesEl.on('click', '.file .remove', function (ev) { $filesEl.on('click', '.file .remove', function (ev) {
ev.preventDefault(); ev.preventDefault()
var name = $(this).parent().find('.name').text(); var name = $(this).parent().find('.name').text()
if (confirm('Are you sure you want to remove: ' + name + ' from local storage?')) { if (confirm('Are you sure you want to remove: ' + name + ' from local storage?')) {
storage.remove(utils.fileKey(name)); storage.remove(utils.fileKey(name))
editor.removeSession(utils.fileKey(name)); editor.removeSession(utils.fileKey(name))
editor.setNextFile(utils.fileKey(name)); editor.setNextFile(utils.fileKey(name))
updateFiles(); updateFiles()
} }
return false; return false
}); })
function swicthToFile (file) { function swicthToFile (file) {
editor.setCacheFile(utils.fileKey(file)); editor.setCacheFile(utils.fileKey(file))
updateFiles(); updateFiles()
} }
function showFileHandler (ev) { function showFileHandler (ev) {
ev.preventDefault(); ev.preventDefault()
swicthToFile($(this).find('.name').text()); swicthToFile($(this).find('.name').text())
return false; return false
} }
function activeFileTab () { function activeFileTab () {
var name = utils.fileNameFromKey(editor.getCacheFile()); var name = utils.fileNameFromKey(editor.getCacheFile())
return $('#files .file').filter(function () { return $(this).find('.name').text() === name; }); return $('#files .file').filter(function () { return $(this).find('.name').text() === name })
} }
function updateFiles () { function updateFiles () {
var $filesEl = $('#files'); var $filesEl = $('#files')
var files = editor.getFiles(); var files = editor.getFiles()
$filesEl.find('.file').remove(); $filesEl.find('.file').remove()
$('#output').empty(); $('#output').empty()
for (var f in files) { for (var f in files) {
$filesEl.append(fileTabTemplate(files[f])); $filesEl.append(fileTabTemplate(files[f]))
} }
if (editor.cacheFileIsPresent()) { if (editor.cacheFileIsPresent()) {
var active = activeFileTab(); var active = activeFileTab()
active.addClass('active'); active.addClass('active')
editor.resetSession(); editor.resetSession()
} }
$('#input').toggle(editor.cacheFileIsPresent()); $('#input').toggle(editor.cacheFileIsPresent())
$('#output').toggle(editor.cacheFileIsPresent()); $('#output').toggle(editor.cacheFileIsPresent())
reAdjust(); reAdjust()
} }
function fileTabTemplate (key) { function fileTabTemplate (key) {
var name = utils.fileNameFromKey(key); var name = utils.fileNameFromKey(key)
return $('<li class="file"><span class="name">' + name + '</span><span class="remove"><i class="fa fa-close"></i></span></li>'); return $('<li class="file"><span class="name">' + name + '</span><span class="remove"><i class="fa fa-close"></i></span></li>')
} }
var $filesWrapper = $('.files-wrapper'); var $filesWrapper = $('.files-wrapper')
var $scrollerRight = $('.scroller-right'); var $scrollerRight = $('.scroller-right')
var $scrollerLeft = $('.scroller-left'); var $scrollerLeft = $('.scroller-left')
function widthOfList () { function widthOfList () {
var itemsWidth = 0; var itemsWidth = 0
$('.file').each(function () { $('.file').each(function () {
var itemWidth = $(this).outerWidth(); var itemWidth = $(this).outerWidth()
itemsWidth += itemWidth; itemsWidth += itemWidth
}); })
return itemsWidth; return itemsWidth
} }
// function widthOfHidden () { // function widthOfHidden () {
...@@ -282,321 +287,321 @@ var run = function () { ...@@ -282,321 +287,321 @@ var run = function () {
// } // }
function widthOfVisible () { function widthOfVisible () {
return $filesWrapper.outerWidth(); return $filesWrapper.outerWidth()
} }
function getLeftPosi () { function getLeftPosi () {
return $filesEl.position().left; return $filesEl.position().left
} }
function activeFilePos () { function activeFilePos () {
var el = $filesEl.find('.active'); var el = $filesEl.find('.active')
var l = el.position().left; var l = el.position().left
return l; return l
} }
function reAdjust () { function reAdjust () {
if (widthOfList() + getLeftPosi() > widthOfVisible()) { if (widthOfList() + getLeftPosi() > widthOfVisible()) {
$scrollerRight.fadeIn('fast'); $scrollerRight.fadeIn('fast')
} else { } else {
$scrollerRight.fadeOut('fast'); $scrollerRight.fadeOut('fast')
} }
if (getLeftPosi() < 0) { if (getLeftPosi() < 0) {
$scrollerLeft.fadeIn('fast'); $scrollerLeft.fadeIn('fast')
} else { } else {
$scrollerLeft.fadeOut('fast'); $scrollerLeft.fadeOut('fast')
$filesEl.animate({ left: getLeftPosi() + 'px' }, 'slow'); $filesEl.animate({ left: getLeftPosi() + 'px' }, 'slow')
} }
} }
$scrollerRight.click(function () { $scrollerRight.click(function () {
var delta = (getLeftPosi() - FILE_SCROLL_DELTA); var delta = (getLeftPosi() - FILE_SCROLL_DELTA)
$filesEl.animate({ left: delta + 'px' }, 'slow', function () { $filesEl.animate({ left: delta + 'px' }, 'slow', function () {
reAdjust(); reAdjust()
}); })
}); })
$scrollerLeft.click(function () { $scrollerLeft.click(function () {
var delta = Math.min((getLeftPosi() + FILE_SCROLL_DELTA), 0); var delta = Math.min((getLeftPosi() + FILE_SCROLL_DELTA), 0)
$filesEl.animate({ left: delta + 'px' }, 'slow', function () { $filesEl.animate({ left: delta + 'px' }, 'slow', function () {
reAdjust(); reAdjust()
}); })
}); })
updateFiles(); updateFiles()
// ----------------- resizeable ui --------------- // ----------------- resizeable ui ---------------
var dragging = false; var dragging = false
$('#dragbar').mousedown(function (e) { $('#dragbar').mousedown(function (e) {
e.preventDefault(); e.preventDefault()
dragging = true; dragging = true
var main = $('#righthand-panel'); var main = $('#righthand-panel')
var ghostbar = $('<div id="ghostbar">', { var ghostbar = $('<div id="ghostbar">', {
css: { css: {
top: main.offset().top, top: main.offset().top,
left: main.offset().left left: main.offset().left
} }
}).prependTo('body'); }).prependTo('body')
$(document).mousemove(function (e) { $(document).mousemove(function (e) {
ghostbar.css('left', e.pageX + 2); ghostbar.css('left', e.pageX + 2)
}); })
}); })
var $body = $('body'); var $body = $('body')
function setEditorSize (delta) { function setEditorSize (delta) {
$('#righthand-panel').css('width', delta); $('#righthand-panel').css('width', delta)
$('#editor').css('right', delta); $('#editor').css('right', delta)
onResize(); onResize()
} }
function getEditorSize () { function getEditorSize () {
storage.setEditorSize($('#righthand-panel').width()); storage.setEditorSize($('#righthand-panel').width())
} }
$(document).mouseup(function (e) { $(document).mouseup(function (e) {
if (dragging) { if (dragging) {
var delta = $body.width() - e.pageX + 2; var delta = $body.width() - e.pageX + 2
$('#ghostbar').remove(); $('#ghostbar').remove()
$(document).unbind('mousemove'); $(document).unbind('mousemove')
dragging = false; dragging = false
setEditorSize(delta); setEditorSize(delta)
storage.setEditorSize(delta); storage.setEditorSize(delta)
reAdjust(); reAdjust()
} }
}); })
// set cached defaults // set cached defaults
var cachedSize = storage.getEditorSize(); var cachedSize = storage.getEditorSize()
if (cachedSize) setEditorSize(cachedSize); if (cachedSize) setEditorSize(cachedSize)
else getEditorSize(); else getEditorSize()
// ----------------- toggle right hand panel ----------------- // ----------------- toggle right hand panel -----------------
var hidingRHP = false; var hidingRHP = false
$('.toggleRHP').click(function () { $('.toggleRHP').click(function () {
hidingRHP = !hidingRHP; hidingRHP = !hidingRHP
setEditorSize(hidingRHP ? 0 : storage.getEditorSize()); setEditorSize(hidingRHP ? 0 : storage.getEditorSize())
$('.toggleRHP i').toggleClass('fa-angle-double-right', !hidingRHP); $('.toggleRHP i').toggleClass('fa-angle-double-right', !hidingRHP)
$('.toggleRHP i').toggleClass('fa-angle-double-left', hidingRHP); $('.toggleRHP i').toggleClass('fa-angle-double-left', hidingRHP)
}); })
// ----------------- editor resize --------------- // ----------------- editor resize ---------------
function onResize () { function onResize () {
editor.resize(); editor.resize()
reAdjust(); reAdjust()
} }
window.onresize = onResize; window.onresize = onResize
onResize(); onResize()
document.querySelector('#editor').addEventListener('change', onResize); document.querySelector('#editor').addEventListener('change', onResize)
document.querySelector('#editorWrap').addEventListener('change', onResize); document.querySelector('#editorWrap').addEventListener('change', onResize)
// ----------------- compiler output renderer ---------------------- // ----------------- compiler output renderer ----------------------
$('.asmOutput button').click(function () { $(this).parent().find('pre').toggle(); }); $('.asmOutput button').click(function () { $(this).parent().find('pre').toggle() })
// ----------------- compiler ---------------------- // ----------------- compiler ----------------------
function handleGithubCall (root, path, cb) { function handleGithubCall (root, path, cb) {
$('#output').append($('<div/>').append($('<pre/>').text('Loading github.com/' + root + '/' + path + ' ...'))); $('#output').append($('<div/>').append($('<pre/>').text('Loading github.com/' + root + '/' + path + ' ...')))
return $.getJSON('https://api.github.com/repos/' + root + '/contents/' + path) return $.getJSON('https://api.github.com/repos/' + root + '/contents/' + path)
.done(function (data) { .done(function (data) {
if ('content' in data) { if ('content' in data) {
cb(null, base64.decode(data.content)); cb(null, base64.decode(data.content))
} else { } else {
cb('Content not received'); cb('Content not received')
} }
}) })
.fail(function (xhr, text, err) { .fail(function (xhr, text, err) {
// NOTE: on some browsers, err equals to '' for certain errors (such as offline browser) // NOTE: on some browsers, err equals to '' for certain errors (such as offline browser)
cb(err || 'Unknown transport error'); cb(err || 'Unknown transport error')
}); })
} }
var executionContext = new ExecutionContext(); var executionContext = new ExecutionContext()
var compiler = new Compiler(editor, handleGithubCall); var compiler = new Compiler(editor, handleGithubCall)
var formalVerification = new FormalVerification($('#verificationView'), compiler.event); var formalVerification = new FormalVerification($('#verificationView'), compiler.event)
var transactionDebugger = new Debugger('#debugger', editor, compiler, executionContext.event, swicthToFile); var transactionDebugger = new Debugger('#debugger', editor, compiler, executionContext.event, swicthToFile)
transactionDebugger.addProvider('vm', executionContext.vm()); transactionDebugger.addProvider('vm', executionContext.vm())
transactionDebugger.switchProvider('vm'); transactionDebugger.switchProvider('vm')
transactionDebugger.addProvider('injected', executionContext.web3()); transactionDebugger.addProvider('injected', executionContext.web3())
transactionDebugger.addProvider('web3', executionContext.web3()); transactionDebugger.addProvider('web3', executionContext.web3())
var udapp = new UniversalDApp(executionContext, { var udapp = new UniversalDApp(executionContext, {
removable: false, removable: false,
removable_instances: true removable_instances: true
}, transactionDebugger); }, transactionDebugger)
udapp.event.register('debugRequested', this, function (txResult) { udapp.event.register('debugRequested', this, function (txResult) {
startdebugging(txResult.transactionHash); startdebugging(txResult.transactionHash)
}); })
var renderer = new Renderer(editor, executionContext.web3(), updateFiles, udapp, executionContext, formalVerification.event, compiler.event); // eslint-disable-line var renderer = new Renderer(editor, executionContext.web3(), updateFiles, udapp, executionContext, formalVerification.event, compiler.event) // eslint-disable-line
var autoCompile = document.querySelector('#autoCompile').checked; var autoCompile = document.querySelector('#autoCompile').checked
document.querySelector('#autoCompile').addEventListener('change', function () { document.querySelector('#autoCompile').addEventListener('change', function () {
autoCompile = document.querySelector('#autoCompile').checked; autoCompile = document.querySelector('#autoCompile').checked
}); })
var previousInput = ''; var previousInput = ''
var compileTimeout = null; var compileTimeout = null
function editorOnChange () { function editorOnChange () {
var input = editor.getValue(); var input = editor.getValue()
if (input === '') { if (input === '') {
editor.setCacheFileContent(''); editor.setCacheFileContent('')
return; return
} }
if (input === previousInput) { if (input === previousInput) {
return; return
} }
previousInput = input; previousInput = input
if (!autoCompile) { if (!autoCompile) {
return; return
} }
if (compileTimeout) { if (compileTimeout) {
window.clearTimeout(compileTimeout); window.clearTimeout(compileTimeout)
} }
compileTimeout = window.setTimeout(compiler.compile, 300); compileTimeout = window.setTimeout(compiler.compile, 300)
} }
editor.onChangeSetup(editorOnChange); editor.onChangeSetup(editorOnChange)
$('#compile').click(function () { $('#compile').click(function () {
compiler.compile(); compiler.compile()
}); })
executionContext.event.register('contextChanged', this, function (context) { executionContext.event.register('contextChanged', this, function (context) {
compiler.compile(); compiler.compile()
}); })
executionContext.event.register('web3EndpointChanged', this, function (context) { executionContext.event.register('web3EndpointChanged', this, function (context) {
compiler.compile(); compiler.compile()
}); })
compiler.event.register('loadingCompiler', this, function (url, usingWorker) { compiler.event.register('loadingCompiler', this, function (url, usingWorker) {
setVersionText(usingWorker ? '(loading using worker)' : '(loading)'); setVersionText(usingWorker ? '(loading using worker)' : '(loading)')
}); })
compiler.event.register('compilerLoaded', this, function (version) { compiler.event.register('compilerLoaded', this, function (version) {
previousInput = ''; previousInput = ''
setVersionText(version); setVersionText(version)
compiler.compile(); compiler.compile()
if (queryParams.get().endpointurl) { if (queryParams.get().endpointurl) {
executionContext.setEndPointUrl(queryParams.get().endpointurl); executionContext.setEndPointUrl(queryParams.get().endpointurl)
} }
if (queryParams.get().context) { if (queryParams.get().context) {
executionContext.setContext(queryParams.get().context); executionContext.setContext(queryParams.get().context)
} }
if (queryParams.get().debugtx) { if (queryParams.get().debugtx) {
startdebugging(queryParams.get().debugtx); startdebugging(queryParams.get().debugtx)
} }
}); })
function startdebugging (txHash) { function startdebugging (txHash) {
transactionDebugger.debug(txHash); transactionDebugger.debug(txHash)
selectTab($('ul#options li.debugView')); selectTab($('ul#options li.debugView'))
} }
function setVersionText (text) { function setVersionText (text) {
$('#version').text(text); $('#version').text(text)
} }
function loadVersion (version) { function loadVersion (version) {
queryParams.update({ version: version }); queryParams.update({ version: version })
var url; var url
if (version === 'builtin') { if (version === 'builtin') {
var location = window.document.location; var location = window.document.location
location = location.protocol + '//' + location.host + '/' + location.pathname; location = location.protocol + '//' + location.host + '/' + location.pathname
if (!location.endsWith('/')) { if (!location.endsWith('/')) {
location += '/'; location += '/'
} }
url = location + 'soljson.js'; url = location + 'soljson.js'
} else { } else {
url = 'https://ethereum.github.io/solc-bin/bin/' + version; url = 'https://ethereum.github.io/solc-bin/bin/' + version
} }
var isFirefox = typeof InstallTrigger !== 'undefined'; var isFirefox = typeof InstallTrigger !== 'undefined'
if (document.location.protocol !== 'file:' && Worker !== undefined && isFirefox) { if (document.location.protocol !== 'file:' && Worker !== undefined && isFirefox) {
// Workers cannot load js on "file:"-URLs and we get a // Workers cannot load js on "file:"-URLs and we get a
// "Uncaught RangeError: Maximum call stack size exceeded" error on Chromium, // "Uncaught RangeError: Maximum call stack size exceeded" error on Chromium,
// resort to non-worker version in that case. // resort to non-worker version in that case.
compiler.loadVersion(true, url); compiler.loadVersion(true, url)
} else { } else {
compiler.loadVersion(false, url); compiler.loadVersion(false, url)
} }
} }
// set default // set default
$('#optimize').attr('checked', (queryParams.get().optimize === 'true')); $('#optimize').attr('checked', (queryParams.get().optimize === 'true'))
compiler.setOptimize(document.querySelector('#optimize').checked); compiler.setOptimize(document.querySelector('#optimize').checked)
document.querySelector('#optimize').addEventListener('change', function () { document.querySelector('#optimize').addEventListener('change', function () {
var optimize = document.querySelector('#optimize').checked; var optimize = document.querySelector('#optimize').checked
queryParams.update({ optimize: optimize }); queryParams.update({ optimize: optimize })
compiler.setOptimize(optimize); compiler.setOptimize(optimize)
compiler.compile(); compiler.compile()
}); })
// ----------------- version selector------------- // ----------------- version selector-------------
// clear and disable the version selector // clear and disable the version selector
$('option', '#versionSelector').remove(); $('option', '#versionSelector').remove()
$('#versionSelector').attr('disabled', true); $('#versionSelector').attr('disabled', true)
// load the new version upon change // load the new version upon change
$('#versionSelector').change(function () { $('#versionSelector').change(function () {
loadVersion($('#versionSelector').val()); loadVersion($('#versionSelector').val())
}); })
$.getJSON('https://ethereum.github.io/solc-bin/bin/list.json').done(function (data) { $.getJSON('https://ethereum.github.io/solc-bin/bin/list.json').done(function (data) {
function buildVersion (build) { function buildVersion (build) {
if (build.prerelease && build.prerelease.length > 0) { if (build.prerelease && build.prerelease.length > 0) {
return build.version + '-' + build.prerelease; return build.version + '-' + build.prerelease
} else { } else {
return build.version; return build.version
} }
} }
// populate version dropdown with all available compiler versions (descending order) // populate version dropdown with all available compiler versions (descending order)
$.each(data.builds.slice().reverse(), function (i, build) { $.each(data.builds.slice().reverse(), function (i, build) {
$('#versionSelector').append(new Option(buildVersion(build), build.path)); $('#versionSelector').append(new Option(buildVersion(build), build.path))
}); })
$('#versionSelector').attr('disabled', false); $('#versionSelector').attr('disabled', false)
// always include the local version // always include the local version
$('#versionSelector').append(new Option('latest local version', 'builtin')); $('#versionSelector').append(new Option('latest local version', 'builtin'))
// find latest release // find latest release
var selectedVersion = data.releases[data.latestRelease]; var selectedVersion = data.releases[data.latestRelease]
// override with the requested version // override with the requested version
if (queryParams.get().version) { if (queryParams.get().version) {
selectedVersion = queryParams.get().version; selectedVersion = queryParams.get().version
} }
loadVersion(selectedVersion); loadVersion(selectedVersion)
}).fail(function (xhr, text, err) { }).fail(function (xhr, text, err) {
// loading failed for some reason, fall back to local compiler // loading failed for some reason, fall back to local compiler
$('#versionSelector').append(new Option('latest local version', 'builtin')); $('#versionSelector').append(new Option('latest local version', 'builtin'))
loadVersion('builtin'); loadVersion('builtin')
}); })
storage.sync(); storage.sync()
}; }
module.exports = { module.exports = {
'run': run 'run': run
}; }
'use strict'; 'use strict'
var solc = require('solc/wrapper'); var solc = require('solc/wrapper')
var compileJSON = function () { return ''; }; var compileJSON = function () { return '' }
var missingInputs = []; var missingInputs = []
module.exports = function (self) { module.exports = function (self) {
self.addEventListener('message', function (e) { self.addEventListener('message', function (e) {
var data = e.data; var data = e.data
switch (data.cmd) { switch (data.cmd) {
case 'loadVersion': case 'loadVersion':
delete self.Module; delete self.Module
// NOTE: workaround some browsers? // NOTE: workaround some browsers?
self.Module = undefined; self.Module = undefined
compileJSON = null; compileJSON = null
self.importScripts(data.data); self.importScripts(data.data)
var compiler = solc(self.Module); var compiler = solc(self.Module)
compileJSON = function (input, optimize) { compileJSON = function (input, optimize) {
try { try {
return JSON.stringify(compiler.compile(JSON.parse(input), optimize, function (path) { return JSON.stringify(compiler.compile(JSON.parse(input), optimize, function (path) {
missingInputs.push(path); missingInputs.push(path)
return { 'error': 'Deferred import' }; return { 'error': 'Deferred import' }
})); }))
} catch (exception) { } catch (exception) {
return JSON.stringify({ error: 'Uncaught JavaScript exception:\n' + exception }); return JSON.stringify({ error: 'Uncaught JavaScript exception:\n' + exception })
}
} }
};
self.postMessage({ self.postMessage({
cmd: 'versionLoaded', cmd: 'versionLoaded',
data: compiler.version(), data: compiler.version(),
acceptsMultipleFiles: compiler.supportsMulti acceptsMultipleFiles: compiler.supportsMulti
}); })
break; break
case 'compile': case 'compile':
missingInputs.length = 0; missingInputs.length = 0
self.postMessage({cmd: 'compiled', job: data.job, data: compileJSON(data.source, data.optimize), missingInputs: missingInputs}); self.postMessage({cmd: 'compiled', job: data.job, data: compileJSON(data.source, data.optimize), missingInputs: missingInputs})
break; break
} }
}, false); }, false)
}; }
'use strict'; 'use strict'
var solc = require('solc/wrapper'); var solc = require('solc/wrapper')
var webworkify = require('webworkify'); var webworkify = require('webworkify')
var utils = require('./utils'); var utils = require('./utils')
var EventManager = require('../lib/eventManager'); var EventManager = require('../lib/eventManager')
/* /*
trigger compilationFinished, compilerLoaded, compilationStarted trigger compilationFinished, compilerLoaded, compilationStarted
*/ */
function Compiler (editor, handleGithubCall) { function Compiler (editor, handleGithubCall) {
var self = this; var self = this
this.event = new EventManager(); this.event = new EventManager()
var compileJSON; var compileJSON
var compilerAcceptsMultipleFiles; var compilerAcceptsMultipleFiles
var cachedRemoteFiles = {}; var cachedRemoteFiles = {}
var worker = null; var worker = null
var optimize = false; var optimize = false
this.setOptimize = function (_optimize) { this.setOptimize = function (_optimize) {
optimize = _optimize; optimize = _optimize
}; }
var compile = function (missingInputs) { var compile = function (missingInputs) {
editor.clearAnnotations(); editor.clearAnnotations()
self.event.trigger('compilationStarted', []); self.event.trigger('compilationStarted', [])
var input = editor.getValue(); var input = editor.getValue()
editor.setCacheFileContent(input); editor.setCacheFileContent(input)
var files = {}; var files = {}
files[utils.fileNameFromKey(editor.getCacheFile())] = input; files[utils.fileNameFromKey(editor.getCacheFile())] = input
gatherImports(files, missingInputs, function (input, error) { gatherImports(files, missingInputs, function (input, error) {
if (input === null) { if (input === null) {
self.lastCompilationResult = null; self.lastCompilationResult = null
self.event.trigger('compilationFinished', [false, { 'error': error }, files]); self.event.trigger('compilationFinished', [false, { 'error': error }, files])
} else { } else {
compileJSON(input, optimize ? 1 : 0); compileJSON(input, optimize ? 1 : 0)
}
})
} }
}); this.compile = compile
};
this.compile = compile;
function setCompileJSON (_compileJSON) { function setCompileJSON (_compileJSON) {
compileJSON = _compileJSON; compileJSON = _compileJSON
} }
this.setCompileJSON = setCompileJSON; // this is exposed for testing this.setCompileJSON = setCompileJSON // this is exposed for testing
function onCompilerLoaded (version) { function onCompilerLoaded (version) {
self.event.trigger('compilerLoaded', [version]); self.event.trigger('compilerLoaded', [version])
} }
function onInternalCompilerLoaded () { function onInternalCompilerLoaded () {
if (worker === null) { if (worker === null) {
var compiler = solc(window.Module); var compiler = solc(window.Module)
compilerAcceptsMultipleFiles = compiler.supportsMulti; compilerAcceptsMultipleFiles = compiler.supportsMulti
compileJSON = function (source, optimize, cb) { compileJSON = function (source, optimize, cb) {
var missingInputs = []; var missingInputs = []
var missingInputsCallback = function (path) { var missingInputsCallback = function (path) {
missingInputs.push(path); missingInputs.push(path)
return { error: 'Deferred import' }; return { error: 'Deferred import' }
}; }
var result; var result
try { try {
result = compiler.compile(source, optimize, missingInputsCallback); result = compiler.compile(source, optimize, missingInputsCallback)
} catch (exception) { } catch (exception) {
result = { error: 'Uncaught JavaScript exception:\n' + exception }; result = { error: 'Uncaught JavaScript exception:\n' + exception }
} }
compilationFinished(result, missingInputs, source); compilationFinished(result, missingInputs, source)
}; }
onCompilerLoaded(compiler.version()); onCompilerLoaded(compiler.version())
} }
} }
this.lastCompilationResult = { this.lastCompilationResult = {
data: null, data: null,
source: null source: null
}; }
function compilationFinished (data, missingInputs, source) { function compilationFinished (data, missingInputs, source) {
var noFatalErrors = true; // ie warnings are ok var noFatalErrors = true // ie warnings are ok
function isValidError (error) { function isValidError (error) {
// The deferred import is not a real error // The deferred import is not a real error
// FIXME: maybe have a better check? // FIXME: maybe have a better check?
if (/Deferred import/.exec(error)) { if (/Deferred import/.exec(error)) {
return false; return false
} }
return utils.errortype(error) !== 'warning'; return utils.errortype(error) !== 'warning'
} }
if (data['error'] !== undefined) { if (data['error'] !== undefined) {
// Ignore warnings (and the 'Deferred import' error as those are generated by us as a workaround // Ignore warnings (and the 'Deferred import' error as those are generated by us as a workaround
if (isValidError(data['error'])) { if (isValidError(data['error'])) {
noFatalErrors = false; noFatalErrors = false
} }
} }
if (data['errors'] !== undefined) { if (data['errors'] !== undefined) {
data['errors'].forEach(function (err) { data['errors'].forEach(function (err) {
// Ignore warnings and the 'Deferred import' error as those are generated by us as a workaround // Ignore warnings and the 'Deferred import' error as those are generated by us as a workaround
if (isValidError(err)) { if (isValidError(err)) {
noFatalErrors = false; noFatalErrors = false
} }
}); })
} }
if (!noFatalErrors) { if (!noFatalErrors) {
// There are fatal errors - abort here // There are fatal errors - abort here
self.lastCompilationResult = null; self.lastCompilationResult = null
self.event.trigger('compilationFinished', [false, data, source]); self.event.trigger('compilationFinished', [false, data, source])
} else if (missingInputs !== undefined && missingInputs.length > 0) { } else if (missingInputs !== undefined && missingInputs.length > 0) {
compile(missingInputs); compile(missingInputs)
} else { } else {
self.lastCompilationResult = { self.lastCompilationResult = {
data: data, data: data,
source: source source: source
}; }
self.event.trigger('compilationFinished', [true, data, source]); self.event.trigger('compilationFinished', [true, data, source])
} }
} }
this.loadVersion = function (usingWorker, url) { this.loadVersion = function (usingWorker, url) {
console.log('Loading ' + url + ' ' + (usingWorker ? 'with worker' : 'without worker')); console.log('Loading ' + url + ' ' + (usingWorker ? 'with worker' : 'without worker'))
self.event.trigger('loadingCompiler', [url, usingWorker]); self.event.trigger('loadingCompiler', [url, usingWorker])
if (usingWorker) { if (usingWorker) {
loadWorker(url); loadWorker(url)
} else { } else {
loadInternal(url); loadInternal(url)
}
} }
};
function loadInternal (url) { function loadInternal (url) {
delete window.Module; delete window.Module
// NOTE: workaround some browsers? // NOTE: workaround some browsers?
window.Module = undefined; window.Module = undefined
// Set a safe fallback until the new one is loaded // Set a safe fallback until the new one is loaded
setCompileJSON(function (source, optimize) { setCompileJSON(function (source, optimize) {
compilationFinished({error: 'Compiler not yet loaded.'}); compilationFinished({error: 'Compiler not yet loaded.'})
}); })
var newScript = document.createElement('script'); var newScript = document.createElement('script')
newScript.type = 'text/javascript'; newScript.type = 'text/javascript'
newScript.src = url; newScript.src = url
document.getElementsByTagName('head')[0].appendChild(newScript); document.getElementsByTagName('head')[0].appendChild(newScript)
var check = window.setInterval(function () { var check = window.setInterval(function () {
if (!window.Module) { if (!window.Module) {
return; return
} }
window.clearInterval(check); window.clearInterval(check)
onInternalCompilerLoaded(); onInternalCompilerLoaded()
}, 200); }, 200)
} }
function loadWorker (url) { function loadWorker (url) {
if (worker !== null) { if (worker !== null) {
worker.terminate(); worker.terminate()
} }
worker = webworkify(require('./compiler-worker.js')); worker = webworkify(require('./compiler-worker.js'))
var jobs = []; var jobs = []
worker.addEventListener('message', function (msg) { worker.addEventListener('message', function (msg) {
var data = msg.data; var data = msg.data
switch (data.cmd) { switch (data.cmd) {
case 'versionLoaded': case 'versionLoaded':
compilerAcceptsMultipleFiles = !!data.acceptsMultipleFiles; compilerAcceptsMultipleFiles = !!data.acceptsMultipleFiles
onCompilerLoaded(data.data); onCompilerLoaded(data.data)
break; break
case 'compiled': case 'compiled':
var result; var result
try { try {
result = JSON.parse(data.data); result = JSON.parse(data.data)
} catch (exception) { } catch (exception) {
result = { 'error': 'Invalid JSON output from the compiler: ' + exception }; result = { 'error': 'Invalid JSON output from the compiler: ' + exception }
} }
var sources = {}; var sources = {}
if (data.job in jobs !== undefined) { if (data.job in jobs !== undefined) {
sources = jobs[data.job].sources; sources = jobs[data.job].sources
delete jobs[data.job]; delete jobs[data.job]
} }
compilationFinished(result, data.missingInputs, sources); compilationFinished(result, data.missingInputs, sources)
break; break
} }
}); })
worker.onerror = function (msg) { worker.onerror = function (msg) {
compilationFinished({ error: 'Worker error: ' + msg.data }); compilationFinished({ error: 'Worker error: ' + msg.data })
}; }
worker.addEventListener('error', function (msg) { worker.addEventListener('error', function (msg) {
compilationFinished({ error: 'Worker error: ' + msg.data }); compilationFinished({ error: 'Worker error: ' + msg.data })
}); })
compileJSON = function (source, optimize) { compileJSON = function (source, optimize) {
jobs.push({sources: source}); jobs.push({sources: source})
worker.postMessage({cmd: 'compile', job: jobs.length - 1, source: JSON.stringify(source), optimize: optimize}); worker.postMessage({cmd: 'compile', job: jobs.length - 1, source: JSON.stringify(source), optimize: optimize})
}; }
worker.postMessage({cmd: 'loadVersion', data: url}); worker.postMessage({cmd: 'loadVersion', data: url})
} }
function gatherImports (files, importHints, cb) { function gatherImports (files, importHints, cb) {
importHints = importHints || []; importHints = importHints || []
if (!compilerAcceptsMultipleFiles) { if (!compilerAcceptsMultipleFiles) {
cb(files[editor.getCacheFile()]); cb(files[editor.getCacheFile()])
return; return
} }
var importRegex = /^\s*import\s*[\'\"]([^\'\"]+)[\'\"];/g; var importRegex = /^\s*import\s*[\'\"]([^\'\"]+)[\'\"];/g
var reloop = false; var reloop = false
var githubMatch; var githubMatch
do { do {
reloop = false; reloop = false
for (var fileName in files) { for (var fileName in files) {
var match; var match
while ((match = importRegex.exec(files[fileName]))) { while ((match = importRegex.exec(files[fileName]))) {
var importFilePath = match[1]; var importFilePath = match[1]
if (importFilePath.startsWith('./')) { if (importFilePath.startsWith('./')) {
importFilePath = importFilePath.slice(2); importFilePath = importFilePath.slice(2)
} }
importHints.push(importFilePath); importHints.push(importFilePath)
} }
} }
while (importHints.length > 0) { while (importHints.length > 0) {
var m = importHints.pop(); var m = importHints.pop()
if (m in files) { if (m in files) {
continue; continue
} }
if (editor.hasFile(m)) { if (editor.hasFile(m)) {
files[m] = editor.getFile(m); files[m] = editor.getFile(m)
reloop = true; reloop = true
} else if (m in cachedRemoteFiles) { } else if (m in cachedRemoteFiles) {
files[m] = cachedRemoteFiles[m]; files[m] = cachedRemoteFiles[m]
reloop = true; 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 (err, content) { handleGithubCall(githubMatch[3], githubMatch[4], function (err, content) {
if (err) { if (err) {
cb(null, 'Unable to import "' + m + '": ' + err); cb(null, 'Unable to import "' + m + '": ' + err)
return; return
} }
cachedRemoteFiles[m] = content; cachedRemoteFiles[m] = content
files[m] = content; files[m] = content
gatherImports(files, importHints, cb); gatherImports(files, importHints, cb)
}); })
return; return
} else if (/^[^:]*:\/\//.exec(m)) { } else if (/^[^:]*:\/\//.exec(m)) {
cb(null, 'Unable to import "' + m + '": Unsupported URL'); cb(null, 'Unable to import "' + m + '": Unsupported URL')
return; return
} else { } else {
cb(null, 'Unable to import "' + m + '": File not found'); cb(null, 'Unable to import "' + m + '": File not found')
return; return
} }
} }
} while (reloop); } while (reloop)
cb({ 'sources': files }); cb({ 'sources': files })
} }
} }
module.exports = Compiler; module.exports = Compiler
'use strict'; 'use strict'
var remix = require('ethereum-remix'); var remix = require('ethereum-remix')
var utils = require('./utils'); var utils = require('./utils')
var ace = require('brace'); var ace = require('brace')
var Range = ace.acequire('ace/range').Range; var Range = ace.acequire('ace/range').Range
/** /**
* Manage remix and source highlighting * Manage remix and source highlighting
*/ */
function Debugger (id, editor, compiler, executionContextEvent, switchToFile) { function Debugger (id, editor, compiler, executionContextEvent, switchToFile) {
this.el = document.querySelector(id); this.el = document.querySelector(id)
this.debugger = new remix.ui.Debugger(); this.debugger = new remix.ui.Debugger()
this.sourceMappingDecoder = new remix.util.SourceMappingDecoder(); this.sourceMappingDecoder = new remix.util.SourceMappingDecoder()
this.el.appendChild(this.debugger.render()); this.el.appendChild(this.debugger.render())
this.editor = editor; this.editor = editor
this.switchToFile = switchToFile; this.switchToFile = switchToFile
this.compiler = compiler; this.compiler = compiler
this.cache = new Cache(); this.cache = new Cache()
var self = this; var self = this
executionContextEvent.register('contextChanged', this, function (context) { executionContextEvent.register('contextChanged', this, function (context) {
self.switchProvider(context); self.switchProvider(context)
}); })
this.lastCompilationResult = null; this.lastCompilationResult = null
this.debugger.register('newTraceLoaded', this, function () { this.debugger.register('newTraceLoaded', this, function () {
self.cache.clear(); self.cache.clear()
self.lastCompilationResult = self.compiler.lastCompilationResult; self.lastCompilationResult = self.compiler.lastCompilationResult
}); })
this.debugger.register('traceUnloaded', this, function () { this.debugger.register('traceUnloaded', this, function () {
self.removeCurrentMarker(); self.removeCurrentMarker()
self.cache.clear(); self.cache.clear()
}); })
this.editor.onChangeSetup(function () { this.editor.onChangeSetup(function () {
if (arguments.length > 0) { // if arguments.length === 0 this is a session change, we don't want to stop debugging in that case if (arguments.length > 0) { // if arguments.length === 0 this is a session change, we don't want to stop debugging in that case
self.debugger.unLoad(); self.debugger.unLoad()
} }
}); })
// register selected code item, highlight the corresponding source location // register selected code item, highlight the corresponding source location
this.debugger.codeManager.register('changed', this, function (code, address, index) { this.debugger.codeManager.register('changed', this, function (code, address, index) {
...@@ -46,16 +46,16 @@ function Debugger (id, editor, compiler, executionContextEvent, switchToFile) { ...@@ -46,16 +46,16 @@ function Debugger (id, editor, compiler, executionContextEvent, switchToFile) {
this.debugger.sourceLocationTracker.getSourceLocation(address, index, self.lastCompilationResult.data.contracts, function (error, rawLocation) { this.debugger.sourceLocationTracker.getSourceLocation(address, index, self.lastCompilationResult.data.contracts, function (error, rawLocation) {
if (!error) { if (!error) {
if (!self.cache.lineBreakPositionsByContent[address]) { if (!self.cache.lineBreakPositionsByContent[address]) {
self.cache.lineBreakPositionsByContent[address] = self.sourceMappingDecoder.getLinebreakPositions(self.editor.getFile(self.lastCompilationResult.data.sourceList[rawLocation.file])); self.cache.lineBreakPositionsByContent[address] = self.sourceMappingDecoder.getLinebreakPositions(self.editor.getFile(self.lastCompilationResult.data.sourceList[rawLocation.file]))
} }
var lineColumnPos = self.sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, self.cache.lineBreakPositionsByContent[address]); var lineColumnPos = self.sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, self.cache.lineBreakPositionsByContent[address])
self.highlight(lineColumnPos, rawLocation); self.highlight(lineColumnPos, rawLocation)
} else { } else {
self.removeCurrentMarker(); self.removeCurrentMarker()
} }
}); })
} }
}); })
} }
/** /**
...@@ -64,13 +64,13 @@ function Debugger (id, editor, compiler, executionContextEvent, switchToFile) { ...@@ -64,13 +64,13 @@ function Debugger (id, editor, compiler, executionContextEvent, switchToFile) {
* @param {String} txHash - hash of the transaction * @param {String} txHash - hash of the transaction
*/ */
Debugger.prototype.debug = function (txHash) { Debugger.prototype.debug = function (txHash) {
var self = this; var self = this
this.debugger.web3().eth.getTransaction(txHash, function (error, tx) { this.debugger.web3().eth.getTransaction(txHash, function (error, tx) {
if (!error) { if (!error) {
self.debugger.debug(tx); self.debugger.debug(tx)
} }
}); })
}; }
/** /**
* highlight the given @arg lineColumnPos * highlight the given @arg lineColumnPos
...@@ -79,15 +79,15 @@ Debugger.prototype.debug = function (txHash) { ...@@ -79,15 +79,15 @@ Debugger.prototype.debug = function (txHash) {
* @param {Object} rawLocation - raw position of the source code to hightlight {start, length, file, jump} * @param {Object} rawLocation - raw position of the source code to hightlight {start, length, file, jump}
*/ */
Debugger.prototype.highlight = function (lineColumnPos, rawLocation) { Debugger.prototype.highlight = function (lineColumnPos, rawLocation) {
var name = utils.fileNameFromKey(this.editor.getCacheFile()); // current opened tab var name = utils.fileNameFromKey(this.editor.getCacheFile()) // current opened tab
var source = this.lastCompilationResult.data.sourceList[rawLocation.file]; // auto switch to that tab var source = this.lastCompilationResult.data.sourceList[rawLocation.file] // auto switch to that tab
this.removeCurrentMarker(); this.removeCurrentMarker()
if (name !== source) { if (name !== source) {
this.switchToFile(source); // command the app to swicth to the next file this.switchToFile(source) // command the app to swicth to the next file
} }
this.currentRange = new Range(lineColumnPos.start.line, lineColumnPos.start.column, lineColumnPos.end.line, lineColumnPos.end.column); this.currentRange = new Range(lineColumnPos.start.line, lineColumnPos.start.column, lineColumnPos.end.line, lineColumnPos.end.column)
this.currentMarker = this.editor.addMarker(this.currentRange, 'highlightcode'); this.currentMarker = this.editor.addMarker(this.currentRange, 'highlightcode')
}; }
/** /**
* add a new web3 provider to remix * add a new web3 provider to remix
...@@ -96,8 +96,8 @@ Debugger.prototype.highlight = function (lineColumnPos, rawLocation) { ...@@ -96,8 +96,8 @@ Debugger.prototype.highlight = function (lineColumnPos, rawLocation) {
* @param {Object} obj - provider * @param {Object} obj - provider
*/ */
Debugger.prototype.addProvider = function (type, obj) { Debugger.prototype.addProvider = function (type, obj) {
this.debugger.addProvider(type, obj); this.debugger.addProvider(type, obj)
}; }
/** /**
* switch the provider * switch the provider
...@@ -105,32 +105,32 @@ Debugger.prototype.addProvider = function (type, obj) { ...@@ -105,32 +105,32 @@ Debugger.prototype.addProvider = function (type, obj) {
* @param {String} type - type/name of the provider to use * @param {String} type - type/name of the provider to use
*/ */
Debugger.prototype.switchProvider = function (type) { Debugger.prototype.switchProvider = function (type) {
this.debugger.switchProvider(type); this.debugger.switchProvider(type)
}; }
/** /**
* get the current provider * get the current provider
*/ */
Debugger.prototype.web3 = function (type) { Debugger.prototype.web3 = function (type) {
return this.debugger.web3(); return this.debugger.web3()
}; }
/** /**
* unhighlight the current highlighted statement * unhighlight the current highlighted statement
*/ */
Debugger.prototype.removeCurrentMarker = function () { Debugger.prototype.removeCurrentMarker = function () {
if (this.currentMarker) { if (this.currentMarker) {
this.editor.removeMarker(this.currentMarker); this.editor.removeMarker(this.currentMarker)
this.currentMarker = null; this.currentMarker = null
} }
}; }
function Cache () { function Cache () {
this.contentLineBreakPosition = {}; this.contentLineBreakPosition = {}
} }
Cache.prototype.clear = function () { Cache.prototype.clear = function () {
this.lineBreakPositionsByContent = {}; this.lineBreakPositionsByContent = {}
}; }
module.exports = Debugger; module.exports = Debugger
/* global FileReader */ /* global FileReader */
'use strict'; 'use strict'
var utils = require('./utils'); var utils = require('./utils')
var examples = require('./example-contracts'); var examples = require('./example-contracts')
var ace = require('brace'); var ace = require('brace')
require('../mode-solidity.js'); require('../mode-solidity.js')
function Editor (loadingFromGist, storage) { function Editor (loadingFromGist, storage) {
var SOL_CACHE_UNTITLED = utils.fileKey('Untitled'); var SOL_CACHE_UNTITLED = utils.fileKey('Untitled')
var SOL_CACHE_FILE = null; var SOL_CACHE_FILE = null
var editor = ace.edit('input'); var editor = ace.edit('input')
var sessions = {}; var sessions = {}
var sourceAnnotations = []; var sourceAnnotations = []
setupStuff(getFiles()); setupStuff(getFiles())
this.addMarker = function (range, cssClass) { this.addMarker = function (range, cssClass) {
return editor.session.addMarker(range, cssClass); return editor.session.addMarker(range, cssClass)
}; }
this.removeMarker = function (markerId) { this.removeMarker = function (markerId) {
editor.session.removeMarker(markerId); editor.session.removeMarker(markerId)
}; }
this.newFile = function () { this.newFile = function () {
var untitledCount = ''; var untitledCount = ''
while (storage.exists(SOL_CACHE_UNTITLED + untitledCount)) { while (storage.exists(SOL_CACHE_UNTITLED + untitledCount)) {
untitledCount = (untitledCount - 0) + 1; untitledCount = (untitledCount - 0) + 1
}
SOL_CACHE_FILE = SOL_CACHE_UNTITLED + untitledCount
this.setCacheFileContent('')
} }
SOL_CACHE_FILE = SOL_CACHE_UNTITLED + untitledCount;
this.setCacheFileContent('');
};
this.uploadFile = function (file, callback) { this.uploadFile = function (file, callback) {
var fileReader = new FileReader(); var fileReader = new FileReader()
var cacheName = utils.fileKey(file.name); var cacheName = utils.fileKey(file.name)
fileReader.onload = function (e) { fileReader.onload = function (e) {
storage.set(cacheName, e.target.result); storage.set(cacheName, e.target.result)
SOL_CACHE_FILE = cacheName; SOL_CACHE_FILE = cacheName
callback(); callback()
}; }
fileReader.readAsText(file); fileReader.readAsText(file)
}; }
this.setCacheFileContent = function (content) { this.setCacheFileContent = function (content) {
storage.set(SOL_CACHE_FILE, content); storage.set(SOL_CACHE_FILE, content)
}; }
this.setCacheFile = function (cacheFile) { this.setCacheFile = function (cacheFile) {
SOL_CACHE_FILE = cacheFile; SOL_CACHE_FILE = cacheFile
}; }
this.getCacheFile = function () { this.getCacheFile = function () {
return SOL_CACHE_FILE; return SOL_CACHE_FILE
}; }
this.cacheFileIsPresent = function () { this.cacheFileIsPresent = function () {
return !!SOL_CACHE_FILE; return !!SOL_CACHE_FILE
}; }
this.setNextFile = function (fileKey) { this.setNextFile = function (fileKey) {
var index = this.getFiles().indexOf(fileKey); var index = this.getFiles().indexOf(fileKey)
this.setCacheFile(this.getFiles()[ Math.max(0, index - 1) ]); this.setCacheFile(this.getFiles()[ Math.max(0, index - 1) ])
}; }
this.resetSession = function () { this.resetSession = function () {
editor.setSession(sessions[SOL_CACHE_FILE]); editor.setSession(sessions[SOL_CACHE_FILE])
editor.focus(); editor.focus()
}; }
this.removeSession = function (fileKey) { this.removeSession = function (fileKey) {
delete sessions[fileKey]; delete sessions[fileKey]
}; }
this.renameSession = function (oldFileKey, newFileKey) { this.renameSession = function (oldFileKey, newFileKey) {
if (oldFileKey !== newFileKey) { if (oldFileKey !== newFileKey) {
sessions[newFileKey] = sessions[oldFileKey]; sessions[newFileKey] = sessions[oldFileKey]
this.removeSession(oldFileKey); this.removeSession(oldFileKey)
}
} }
};
this.hasFile = function (name) { this.hasFile = function (name) {
return this.getFiles().indexOf(utils.fileKey(name)) !== -1; return this.getFiles().indexOf(utils.fileKey(name)) !== -1
}; }
this.getFile = function (name) { this.getFile = function (name) {
return storage.get(utils.fileKey(name)); return storage.get(utils.fileKey(name))
}; }
function getFiles () { function getFiles () {
var files = []; var files = []
storage.keys().forEach(function (f) { storage.keys().forEach(function (f) {
if (utils.isCachedFile(f)) { if (utils.isCachedFile(f)) {
files.push(f); files.push(f)
if (!sessions[f]) sessions[f] = newEditorSession(f); if (!sessions[f]) sessions[f] = newEditorSession(f)
} }
}); })
return files; return files
} }
this.getFiles = getFiles; this.getFiles = getFiles
this.packageFiles = function () { this.packageFiles = function () {
var files = {}; var files = {}
var filesArr = this.getFiles(); var filesArr = this.getFiles()
for (var f in filesArr) { for (var f in filesArr) {
files[utils.fileNameFromKey(filesArr[f])] = { files[utils.fileNameFromKey(filesArr[f])] = {
content: storage.get(filesArr[f]) content: storage.get(filesArr[f])
};
} }
return files; }
}; return files
}
this.resize = function () { this.resize = function () {
editor.resize(); editor.resize()
var session = editor.getSession(); var session = editor.getSession()
session.setUseWrapMode(document.querySelector('#editorWrap').checked); session.setUseWrapMode(document.querySelector('#editorWrap').checked)
if (session.getUseWrapMode()) { if (session.getUseWrapMode()) {
var characterWidth = editor.renderer.characterWidth; var characterWidth = editor.renderer.characterWidth
var contentWidth = editor.container.ownerDocument.getElementsByClassName('ace_scroller')[0].clientWidth; var contentWidth = editor.container.ownerDocument.getElementsByClassName('ace_scroller')[0].clientWidth
if (contentWidth > 0) { if (contentWidth > 0) {
session.setWrapLimit(parseInt(contentWidth / characterWidth, 10)); session.setWrapLimit(parseInt(contentWidth / characterWidth, 10))
}
} }
} }
};
this.getValue = function () { this.getValue = function () {
return editor.getValue(); return editor.getValue()
}; }
this.clearAnnotations = function () { this.clearAnnotations = function () {
sourceAnnotations = []; sourceAnnotations = []
editor.getSession().clearAnnotations(); editor.getSession().clearAnnotations()
}; }
this.addAnnotation = function (annotation) { this.addAnnotation = function (annotation) {
sourceAnnotations[sourceAnnotations.length] = annotation; sourceAnnotations[sourceAnnotations.length] = annotation
this.setAnnotations(sourceAnnotations); this.setAnnotations(sourceAnnotations)
}; }
this.setAnnotations = function (sourceAnnotations) { this.setAnnotations = function (sourceAnnotations) {
editor.getSession().setAnnotations(sourceAnnotations); editor.getSession().setAnnotations(sourceAnnotations)
}; }
this.onChangeSetup = function (onChange) { this.onChangeSetup = function (onChange) {
editor.getSession().on('change', onChange); editor.getSession().on('change', onChange)
editor.on('changeSession', function () { editor.on('changeSession', function () {
editor.getSession().on('change', onChange); editor.getSession().on('change', onChange)
onChange(); onChange()
}); })
}; }
this.handleErrorClick = function (errLine, errCol) { this.handleErrorClick = function (errLine, errCol) {
editor.focus(); editor.focus()
editor.gotoLine(errLine + 1, errCol - 1, true); editor.gotoLine(errLine + 1, errCol - 1, true)
}; }
function newEditorSession (filekey) { function newEditorSession (filekey) {
var s = new ace.EditSession(storage.get(filekey), 'ace/mode/javascript'); var s = new ace.EditSession(storage.get(filekey), 'ace/mode/javascript')
s.setUndoManager(new ace.UndoManager()); s.setUndoManager(new ace.UndoManager())
s.setTabSize(4); s.setTabSize(4)
s.setUseSoftTabs(true); s.setUseSoftTabs(true)
sessions[filekey] = s; sessions[filekey] = s
return s; return s
} }
function setupStuff (files) { function setupStuff (files) {
if (files.length === 0) { if (files.length === 0) {
if (loadingFromGist) return; if (loadingFromGist) return
files.push(utils.fileKey(examples.ballot.name)); files.push(utils.fileKey(examples.ballot.name))
storage.set(utils.fileKey(examples.ballot.name), examples.ballot.content); storage.set(utils.fileKey(examples.ballot.name), examples.ballot.content)
} }
SOL_CACHE_FILE = files[0]; SOL_CACHE_FILE = files[0]
for (var x in files) { for (var x in files) {
sessions[files[x]] = newEditorSession(files[x]); sessions[files[x]] = newEditorSession(files[x])
} }
editor.setSession(sessions[SOL_CACHE_FILE]); editor.setSession(sessions[SOL_CACHE_FILE])
editor.resize(true); editor.resize(true)
// Unmap ctrl-t & ctrl-f // Unmap ctrl-t & ctrl-f
editor.commands.bindKeys({ 'ctrl-t': null }); editor.commands.bindKeys({ 'ctrl-t': null })
editor.commands.bindKeys({ 'ctrl-f': null }); editor.commands.bindKeys({ 'ctrl-f': null })
} }
} }
module.exports = Editor; module.exports = Editor
var ballot = `pragma solidity ^0.4.0; var ballot = `pragma solidity ^0.4.0
contract Ballot { contract Ballot {
struct Voter { struct Voter {
uint weight; uint weight
bool voted; bool voted
uint8 vote; uint8 vote
address delegate; address delegate
} }
struct Proposal { struct Proposal {
uint voteCount; uint voteCount
} }
address chairperson; address chairperson
mapping(address => Voter) voters; mapping(address => Voter) voters
Proposal[] proposals; Proposal[] proposals
/// Create a new ballot with $(_numProposals) different proposals. /// Create a new ballot with $(_numProposals) different proposals.
function Ballot(uint8 _numProposals) { function Ballot(uint8 _numProposals) {
chairperson = msg.sender; chairperson = msg.sender
voters[chairperson].weight = 1; voters[chairperson].weight = 1
proposals.length = _numProposals; proposals.length = _numProposals
} }
/// Give $(voter) the right to vote on this ballot. /// Give $(voter) the right to vote on this ballot.
/// May only be called by $(chairperson). /// May only be called by $(chairperson).
function giveRightToVote(address voter) { function giveRightToVote(address voter) {
if (msg.sender != chairperson || voters[voter].voted) return; if (msg.sender != chairperson || voters[voter].voted) return
voters[voter].weight = 1; voters[voter].weight = 1
} }
/// Delegate your vote to the voter $(to). /// Delegate your vote to the voter $(to).
function delegate(address to) { function delegate(address to) {
Voter sender = voters[msg.sender]; // assigns reference Voter sender = voters[msg.sender] // assigns reference
if (sender.voted) return; if (sender.voted) return
while (voters[to].delegate != address(0) && voters[to].delegate != msg.sender) while (voters[to].delegate != address(0) && voters[to].delegate != msg.sender)
to = voters[to].delegate; to = voters[to].delegate
if (to == msg.sender) return; if (to == msg.sender) return
sender.voted = true; sender.voted = true
sender.delegate = to; sender.delegate = to
Voter delegate = voters[to]; Voter delegate = voters[to]
if (delegate.voted) if (delegate.voted)
proposals[delegate.vote].voteCount += sender.weight; proposals[delegate.vote].voteCount += sender.weight
else else
delegate.weight += sender.weight; delegate.weight += sender.weight
} }
/// Give a single vote to proposal $(proposal). /// Give a single vote to proposal $(proposal).
function vote(uint8 proposal) { function vote(uint8 proposal) {
Voter sender = voters[msg.sender]; Voter sender = voters[msg.sender]
if (sender.voted || proposal >= proposals.length) return; if (sender.voted || proposal >= proposals.length) return
sender.voted = true; sender.voted = true
sender.vote = proposal; sender.vote = proposal
proposals[proposal].voteCount += sender.weight; proposals[proposal].voteCount += sender.weight
} }
function winningProposal() constant returns (uint8 winningProposal) { function winningProposal() constant returns (uint8 winningProposal) {
uint256 winningVoteCount = 0; uint256 winningVoteCount = 0
for (uint8 proposal = 0; proposal < proposals.length; proposal++) for (uint8 proposal = 0; proposal < proposals.length; proposal++)
if (proposals[proposal].voteCount > winningVoteCount) { if (proposals[proposal].voteCount > winningVoteCount) {
winningVoteCount = proposals[proposal].voteCount; winningVoteCount = proposals[proposal].voteCount
winningProposal = proposal; winningProposal = proposal
} }
} }
}`; }`
module.exports = { module.exports = {
ballot: { name: 'ballot.sol', content: ballot } ballot: { name: 'ballot.sol', content: ballot }
}; }
/* global confirm */ /* global confirm */
'use strict'; 'use strict'
var $ = require('jquery'); var $ = require('jquery')
var Web3 = require('web3'); var Web3 = require('web3')
var EventManager = require('../lib/eventManager'); var EventManager = require('../lib/eventManager')
var EthJSVM = require('ethereumjs-vm'); var EthJSVM = require('ethereumjs-vm')
var injectedProvider; var injectedProvider
var web3; var web3
if (typeof window.web3 !== 'undefined') { if (typeof window.web3 !== 'undefined') {
injectedProvider = window.web3.currentProvider; injectedProvider = window.web3.currentProvider
web3 = new Web3(injectedProvider); web3 = new Web3(injectedProvider)
} else { } else {
web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')); web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'))
} }
var vm = new EthJSVM(null, null, { activatePrecompiles: true, enableHomestead: true }); var vm = new EthJSVM(null, null, { activatePrecompiles: true, enableHomestead: true })
vm.stateManager.checkpoint(); vm.stateManager.checkpoint()
/* /*
trigger contextChanged, web3EndpointChanged trigger contextChanged, web3EndpointChanged
*/ */
function ExecutionContext () { function ExecutionContext () {
var self = this; var self = this
this.event = new EventManager(); this.event = new EventManager()
var executionContext = injectedProvider ? 'injected' : 'vm'; var executionContext = injectedProvider ? 'injected' : 'vm'
this.isVM = function () { this.isVM = function () {
return executionContext === 'vm'; return executionContext === 'vm'
}; }
this.web3 = function () { this.web3 = function () {
return web3; return web3
}; }
this.vm = function () { this.vm = function () {
return vm; return vm
}; }
this.setEndPointUrl = function (url) { this.setEndPointUrl = function (url) {
$web3endpoint.val(url); $web3endpoint.val(url)
}; }
this.setContext = function (context) { this.setContext = function (context) {
executionContext = context; executionContext = context
executionContextChange(context); executionContextChange(context)
setExecutionContextRadio(); setExecutionContextRadio()
}; }
var $injectedToggle = $('#injected-mode'); var $injectedToggle = $('#injected-mode')
var $vmToggle = $('#vm-mode'); var $vmToggle = $('#vm-mode')
var $web3Toggle = $('#web3-mode'); var $web3Toggle = $('#web3-mode')
var $web3endpoint = $('#web3Endpoint'); var $web3endpoint = $('#web3Endpoint')
if (web3.providers && web3.currentProvider instanceof web3.providers.IpcProvider) { if (web3.providers && web3.currentProvider instanceof web3.providers.IpcProvider) {
$web3endpoint.val('ipc'); $web3endpoint.val('ipc')
} }
setExecutionContextRadio(); setExecutionContextRadio()
$injectedToggle.on('change', executionContextUIChange); $injectedToggle.on('change', executionContextUIChange)
$vmToggle.on('change', executionContextUIChange); $vmToggle.on('change', executionContextUIChange)
$web3Toggle.on('change', executionContextUIChange); $web3Toggle.on('change', executionContextUIChange)
$web3endpoint.on('change', function () { $web3endpoint.on('change', function () {
setProviderFromEndpoint(); setProviderFromEndpoint()
if (executionContext === 'web3') { if (executionContext === 'web3') {
self.event.trigger('web3EndpointChanged'); self.event.trigger('web3EndpointChanged')
} }
}); })
function executionContextUIChange (ev) { function executionContextUIChange (ev) {
executionContextChange(ev.target.value); executionContextChange(ev.target.value)
} }
function executionContextChange (context) { function executionContextChange (context) {
if (context === 'web3' && !confirm('Are you sure you want to connect to a local ethereum node?')) { if (context === 'web3' && !confirm('Are you sure you want to connect to a local ethereum node?')) {
setExecutionContextRadio(); setExecutionContextRadio()
} else if (context === 'injected' && injectedProvider === undefined) { } else if (context === 'injected' && injectedProvider === undefined) {
setExecutionContextRadio(); setExecutionContextRadio()
} else { } else {
if (context === 'web3') { if (context === 'web3') {
executionContext = context; executionContext = context
setProviderFromEndpoint(); setProviderFromEndpoint()
self.event.trigger('contextChanged', ['web3']); self.event.trigger('contextChanged', ['web3'])
} else if (context === 'injected') { } else if (context === 'injected') {
executionContext = context; executionContext = context
web3.setProvider(injectedProvider); web3.setProvider(injectedProvider)
self.event.trigger('contextChanged', ['injected']); self.event.trigger('contextChanged', ['injected'])
} else if (context === 'vm') { } else if (context === 'vm') {
executionContext = context; executionContext = context
vm.stateManager.revert(function () { vm.stateManager.revert(function () {
vm.stateManager.checkpoint(); vm.stateManager.checkpoint()
}); })
self.event.trigger('contextChanged', ['vm']); self.event.trigger('contextChanged', ['vm'])
} }
} }
} }
function setProviderFromEndpoint () { function setProviderFromEndpoint () {
var endpoint = $web3endpoint.val(); var endpoint = $web3endpoint.val()
if (endpoint === 'ipc') { if (endpoint === 'ipc') {
web3.setProvider(new web3.providers.IpcProvider()); web3.setProvider(new web3.providers.IpcProvider())
} else { } else {
web3.setProvider(new web3.providers.HttpProvider(endpoint)); web3.setProvider(new web3.providers.HttpProvider(endpoint))
} }
} }
function setExecutionContextRadio () { function setExecutionContextRadio () {
if (executionContext === 'injected') { if (executionContext === 'injected') {
$injectedToggle.get(0).checked = true; $injectedToggle.get(0).checked = true
} else if (executionContext === 'vm') { } else if (executionContext === 'vm') {
$vmToggle.get(0).checked = true; $vmToggle.get(0).checked = true
} else if (executionContext === 'web3') { } else if (executionContext === 'web3') {
$web3Toggle.get(0).checked = true; $web3Toggle.get(0).checked = true
} }
} }
} }
module.exports = ExecutionContext; module.exports = ExecutionContext
'use strict'; 'use strict'
var $ = require('jquery'); var $ = require('jquery')
var EventManager = require('../lib/eventManager'); var EventManager = require('../lib/eventManager')
/* /*
trigger compilationFinished trigger compilationFinished
*/ */
function FormalVerification (outputElement, compilerEvent) { function FormalVerification (outputElement, compilerEvent) {
this.event = new EventManager(); this.event = new EventManager()
this.outputElement = outputElement; this.outputElement = outputElement
var self = this; var self = this
compilerEvent.register('compilationFinished', this, function (success, data, source) { compilerEvent.register('compilationFinished', this, function (success, data, source) {
if (success) { if (success) {
self.compilationFinished(data); self.compilationFinished(data)
} }
}); })
compilerEvent.register('compilationStarted', this, function () { compilerEvent.register('compilationStarted', this, function () {
$('#formalVerificationInput', self.outputElement) $('#formalVerificationInput', self.outputElement)
.val('') .val('')
.hide(); .hide()
$('#formalVerificationErrors').empty(); $('#formalVerificationErrors').empty()
}); })
} }
FormalVerification.prototype.compilationFinished = function (compilationResult) { FormalVerification.prototype.compilationFinished = function (compilationResult) {
if (compilationResult.formal === undefined) { if (compilationResult.formal === undefined) {
this.event.trigger('compilationFinished', [false, 'Formal verification not supported by this compiler version.', $('#formalVerificationErrors'), true]); this.event.trigger('compilationFinished', [false, 'Formal verification not supported by this compiler version.', $('#formalVerificationErrors'), true])
} else { } else {
if (compilationResult.formal['why3'] !== undefined) { if (compilationResult.formal['why3'] !== undefined) {
$('#formalVerificationInput', this.outputElement).val( $('#formalVerificationInput', this.outputElement).val(
'(* copy this to http://why3.lri.fr/try/ *)' + '(* copy this to http://why3.lri.fr/try/ *)' +
compilationResult.formal['why3'] compilationResult.formal['why3']
) )
.show(); .show()
} }
if (compilationResult.formal.errors !== undefined) { if (compilationResult.formal.errors !== undefined) {
var errors = compilationResult.formal.errors; var errors = compilationResult.formal.errors
for (var i = 0; i < errors.length; i++) { for (var i = 0; i < errors.length; i++) {
this.event.trigger('compilationFinished', [false, errors[i], $('#formalVerificationErrors'), true]); this.event.trigger('compilationFinished', [false, errors[i], $('#formalVerificationErrors'), true])
} }
} else { } else {
this.event.trigger('compilationFinished', [true, null, null, true]); this.event.trigger('compilationFinished', [true, null, null, true])
} }
} }
}; }
module.exports = FormalVerification; module.exports = FormalVerification
'use strict'; 'use strict'
// Allowing window to be overriden for testing // Allowing window to be overriden for testing
function GistHandler (_window) { function GistHandler (_window) {
if (_window === undefined) _window = window; if (_window === undefined) _window = window
this.handleLoad = function (params, cb) { this.handleLoad = function (params, cb) {
var loadingFromGist = false; var loadingFromGist = false
var gistId; var gistId
if (params['gist'] === '') { if (params['gist'] === '') {
var str = _window.prompt('Enter the URL or ID of the Gist you would like to load.'); var str = _window.prompt('Enter the URL or ID of the Gist you would like to load.')
if (str !== '') { if (str !== '') {
gistId = getGistId(str); gistId = getGistId(str)
loadingFromGist = !!gistId; loadingFromGist = !!gistId
} }
} else { } else {
gistId = params['gist']; gistId = params['gist']
loadingFromGist = !!gistId; loadingFromGist = !!gistId
} }
if (loadingFromGist) { if (loadingFromGist) {
cb(gistId); cb(gistId)
}
return loadingFromGist
} }
return loadingFromGist;
};
function getGistId (str) { function getGistId (str) {
var idr = /[0-9A-Fa-f]{8,}/; var idr = /[0-9A-Fa-f]{8,}/
var match = idr.exec(str); var match = idr.exec(str)
return match ? match[0] : null; return match ? match[0] : null
} }
} }
module.exports = GistHandler; module.exports = GistHandler
'use strict'; 'use strict'
// Allowing window to be overriden for testing // Allowing window to be overriden for testing
function QueryParams (_window) { function QueryParams (_window) {
if (_window === undefined) _window = window; if (_window === undefined) _window = window
this.get = function () { this.get = function () {
var qs = _window.location.hash.substr(1); var qs = _window.location.hash.substr(1)
if (_window.location.search.length > 0) { if (_window.location.search.length > 0) {
// use legacy query params instead of hash // use legacy query params instead of hash
_window.location.hash = _window.location.search.substr(1); _window.location.hash = _window.location.search.substr(1)
_window.location.search = ''; _window.location.search = ''
} }
var params = {}; var params = {}
var parts = qs.split('&'); var parts = qs.split('&')
for (var x in parts) { for (var x in parts) {
var keyValue = parts[x].split('='); var keyValue = parts[x].split('=')
if (keyValue[0] !== '') { if (keyValue[0] !== '') {
params[keyValue[0]] = keyValue[1]; params[keyValue[0]] = keyValue[1]
} }
} }
return params; return params
}; }
this.update = function (params) { this.update = function (params) {
var currentParams = this.get(); var currentParams = this.get()
var keys = Object.keys(params); var keys = Object.keys(params)
for (var x in keys) { for (var x in keys) {
currentParams[keys[x]] = params[keys[x]]; currentParams[keys[x]] = params[keys[x]]
} }
var queryString = '#'; var queryString = '#'
var updatedKeys = Object.keys(currentParams); var updatedKeys = Object.keys(currentParams)
for (var y in updatedKeys) { for (var y in updatedKeys) {
queryString += updatedKeys[y] + '=' + currentParams[updatedKeys[y]] + '&'; queryString += updatedKeys[y] + '=' + currentParams[updatedKeys[y]] + '&'
}
_window.location.hash = queryString.slice(0, -1)
} }
_window.location.hash = queryString.slice(0, -1);
};
} }
module.exports = QueryParams; module.exports = QueryParams
'use strict'; 'use strict'
var $ = require('jquery'); var $ = require('jquery')
var utils = require('./utils'); var utils = require('./utils')
var uiHelper = require('./ui-helper'); var uiHelper = require('./ui-helper')
function Renderer (editor, web3, updateFiles, udapp, executionContext, formalVerificationEvent, compilerEvent) { function Renderer (editor, web3, updateFiles, udapp, executionContext, formalVerificationEvent, compilerEvent) {
this.editor = editor; this.editor = editor
this.web3 = web3; this.web3 = web3
this.updateFiles = updateFiles; this.updateFiles = updateFiles
this.udapp = udapp; this.udapp = udapp
this.executionContext = executionContext; this.executionContext = executionContext
var self = this; var self = this
formalVerificationEvent.register('compilationFinished', this, function (success, message, container, noAnnotations) { formalVerificationEvent.register('compilationFinished', this, function (success, message, container, noAnnotations) {
if (!success) { if (!success) {
self.error(message, container, noAnnotations); self.error(message, container, noAnnotations)
} }
}); })
compilerEvent.register('compilationFinished', this, function (success, data, source) { compilerEvent.register('compilationFinished', this, function (success, data, source) {
$('#output').empty(); $('#output').empty()
if (success) { if (success) {
self.contracts(data, source); self.contracts(data, source)
} }
// NOTE: still need to display as there might be warnings // NOTE: still need to display as there might be warnings
if (data['error']) { if (data['error']) {
self.error(data['error']); self.error(data['error'])
} }
if (data['errors']) { if (data['errors']) {
data['errors'].forEach(function (err) { data['errors'].forEach(function (err) {
self.error(err); self.error(err)
}); })
} }
}); })
} }
Renderer.prototype.error = function (message, container, noAnnotations) { Renderer.prototype.error = function (message, container, noAnnotations) {
var self = this; var self = this
var type = utils.errortype(message); var type = utils.errortype(message)
var $pre = $('<pre />').text(message); var $pre = $('<pre />').text(message)
var $error = $('<div class="sol ' + type + '"><div class="close"><i class="fa fa-close"></i></div></div>').prepend($pre); var $error = $('<div class="sol ' + type + '"><div class="close"><i class="fa fa-close"></i></div></div>').prepend($pre)
if (container === undefined) { if (container === undefined) {
container = $('#output'); container = $('#output')
} }
container.append($error); container.append($error)
var err = message.match(/^([^:]*):([0-9]*):(([0-9]*):)? /); var err = message.match(/^([^:]*):([0-9]*):(([0-9]*):)? /)
if (err) { if (err) {
var errFile = err[1]; var errFile = err[1]
var errLine = parseInt(err[2], 10) - 1; var errLine = parseInt(err[2], 10) - 1
var errCol = err[4] ? parseInt(err[4], 10) : 0; var errCol = err[4] ? parseInt(err[4], 10) : 0
if (!noAnnotations && (errFile === '' || errFile === utils.fileNameFromKey(self.editor.getCacheFile()))) { if (!noAnnotations && (errFile === '' || errFile === utils.fileNameFromKey(self.editor.getCacheFile()))) {
self.editor.addAnnotation({ self.editor.addAnnotation({
row: errLine, row: errLine,
column: errCol, column: errCol,
text: message, text: message,
type: type type: type
}); })
} }
$error.click(function (ev) { $error.click(function (ev) {
if (errFile !== '' && errFile !== utils.fileNameFromKey(self.editor.getCacheFile()) && self.editor.hasFile(errFile)) { if (errFile !== '' && errFile !== utils.fileNameFromKey(self.editor.getCacheFile()) && self.editor.hasFile(errFile)) {
// Switch to file // Switch to file
self.editor.setCacheFile(utils.fileKey(errFile)); self.editor.setCacheFile(utils.fileKey(errFile))
self.updateFiles(); self.updateFiles()
// @TODO could show some error icon in files with errors // @TODO could show some error icon in files with errors
} }
self.editor.handleErrorClick(errLine, errCol); self.editor.handleErrorClick(errLine, errCol)
}); })
$error.find('.close').click(function (ev) { $error.find('.close').click(function (ev) {
ev.preventDefault(); ev.preventDefault()
$error.remove(); $error.remove()
return false; return false
}); })
} }
}; }
Renderer.prototype.contracts = function (data, source) { Renderer.prototype.contracts = function (data, source) {
var udappContracts = []; var udappContracts = []
for (var contractName in data.contracts) { for (var contractName in data.contracts) {
var contract = data.contracts[contractName]; var contract = data.contracts[contractName]
udappContracts.push({ udappContracts.push({
name: contractName, name: contractName,
interface: contract['interface'], interface: contract['interface'],
bytecode: contract.bytecode bytecode: contract.bytecode
}); })
} }
// rendering function used by udapp. they need data and source // rendering function used by udapp. they need data and source
var combined = function (contractName, jsonInterface, bytecode) { var combined = function (contractName, jsonInterface, bytecode) {
return JSON.stringify([{ name: contractName, interface: jsonInterface, bytecode: bytecode }]); return JSON.stringify([{ name: contractName, interface: jsonInterface, bytecode: bytecode }])
}; }
var renderOutputModifier = function (contractName, $contractOutput) { var renderOutputModifier = function (contractName, $contractOutput) {
var contract = data.contracts[contractName]; var contract = data.contracts[contractName]
if (contract.bytecode) { if (contract.bytecode) {
$contractOutput.append(uiHelper.textRow('Bytecode', contract.bytecode)); $contractOutput.append(uiHelper.textRow('Bytecode', contract.bytecode))
} }
$contractOutput.append(uiHelper.textRow('Interface', contract['interface'])); $contractOutput.append(uiHelper.textRow('Interface', contract['interface']))
if (contract.bytecode) { if (contract.bytecode) {
$contractOutput.append(uiHelper.textRow('Web3 deploy', uiHelper.gethDeploy(contractName.toLowerCase(), contract['interface'], contract.bytecode), 'deploy')); $contractOutput.append(uiHelper.textRow('Web3 deploy', uiHelper.gethDeploy(contractName.toLowerCase(), contract['interface'], contract.bytecode), 'deploy'))
$contractOutput.append(uiHelper.textRow('uDApp', combined(contractName, contract['interface'], contract.bytecode), 'deploy')); $contractOutput.append(uiHelper.textRow('uDApp', combined(contractName, contract['interface'], contract.bytecode), 'deploy'))
}
var ctrSource = getSource(contractName, source, data)
return $contractOutput.append(uiHelper.getDetails(contract, ctrSource, contractName))
} }
var ctrSource = getSource(contractName, source, data);
return $contractOutput.append(uiHelper.getDetails(contract, ctrSource, contractName));
};
// // // //
var self = this; var self = this
var getSource = function (contractName, source, data) { var getSource = function (contractName, source, data) {
var currentFile = utils.fileNameFromKey(self.editor.getCacheFile()); var currentFile = utils.fileNameFromKey(self.editor.getCacheFile())
return source.sources[currentFile]; return source.sources[currentFile]
}; }
var getAddress = function () { return $('#txorigin').val(); }; var getAddress = function () { return $('#txorigin').val() }
var getValue = function () { var getValue = function () {
var comp = $('#value').val().split(' '); var comp = $('#value').val().split(' ')
return self.executionContext.web3().toWei(comp[0], comp.slice(1).join(' ')); return self.executionContext.web3().toWei(comp[0], comp.slice(1).join(' '))
}; }
var getGasLimit = function () { return $('#gasLimit').val(); }; var getGasLimit = function () { return $('#gasLimit').val() }
this.udapp.reset(udappContracts, getAddress, getValue, getGasLimit, renderOutputModifier); this.udapp.reset(udappContracts, getAddress, getValue, getGasLimit, renderOutputModifier)
var $contractOutput = this.udapp.render(); var $contractOutput = this.udapp.render()
var $txOrigin = $('#txorigin'); var $txOrigin = $('#txorigin')
this.udapp.getAccounts(function (err, accounts) { this.udapp.getAccounts(function (err, accounts) {
if (err) { if (err) {
self.error(err.message); self.error(err.message)
} }
if (accounts && accounts[0]) { if (accounts && accounts[0]) {
$txOrigin.empty(); $txOrigin.empty()
for (var a in accounts) { $txOrigin.append($('<option />').val(accounts[a]).text(accounts[a])); } for (var a in accounts) { $txOrigin.append($('<option />').val(accounts[a]).text(accounts[a])) }
$txOrigin.val(accounts[0]); $txOrigin.val(accounts[0])
} else { } else {
$txOrigin.val('unknown'); $txOrigin.val('unknown')
} }
}); })
$contractOutput.find('.title').click(function (ev) { $(this).closest('.contract').toggleClass('hide'); }); $contractOutput.find('.title').click(function (ev) { $(this).closest('.contract').toggleClass('hide') })
$('#output').append($contractOutput); $('#output').append($contractOutput)
$('.col2 input,textarea').click(function () { this.select(); }); $('.col2 input,textarea').click(function () { this.select() })
}; }
module.exports = Renderer; module.exports = Renderer
/* global chrome, confirm */ /* global chrome, confirm */
'use strict'; 'use strict'
var utils = require('./utils'); var utils = require('./utils')
function Storage (updateFiles) { function Storage (updateFiles) {
var EDITOR_SIZE_CACHE_KEY = 'editor-size-cache'; var EDITOR_SIZE_CACHE_KEY = 'editor-size-cache'
this.rename = function (originalName, newName) { this.rename = function (originalName, newName) {
var content = this.get(originalName); var content = this.get(originalName)
this.set(newName, content); this.set(newName, content)
this.remove(originalName); this.remove(originalName)
}; }
this.remove = function (name) { this.remove = function (name) {
window.localStorage.removeItem(name); window.localStorage.removeItem(name)
}; }
this.setEditorSize = function (size) { this.setEditorSize = function (size) {
this.set(EDITOR_SIZE_CACHE_KEY, size); this.set(EDITOR_SIZE_CACHE_KEY, size)
}; }
this.getEditorSize = function () { this.getEditorSize = function () {
return this.get(EDITOR_SIZE_CACHE_KEY); return this.get(EDITOR_SIZE_CACHE_KEY)
}; }
this.getFileContent = function (key) { this.getFileContent = function (key) {
return this.get(utils.fileKey(key)); return this.get(utils.fileKey(key))
}; }
this.exists = function (key) { this.exists = function (key) {
return !!this.get(key); return !!this.get(key)
}; }
this.set = function (key, content) { this.set = function (key, content) {
window.localStorage.setItem(key, content); window.localStorage.setItem(key, content)
}; }
this.get = function (key) { this.get = function (key) {
return window.localStorage.getItem(key); return window.localStorage.getItem(key)
}; }
this.keys = function () { this.keys = function () {
// NOTE: this is a workaround for some browsers // NOTE: this is a workaround for some browsers
return Object.keys(window.localStorage).filter(function (item) { return item !== null && item !== undefined; }); return Object.keys(window.localStorage).filter(function (item) { return item !== null && item !== undefined })
}; }
this.loadFile = function (filename, content) { this.loadFile = function (filename, content) {
if (this.exists(filename) && this.get(filename) !== content) { if (this.exists(filename) && this.get(filename) !== content) {
var count = ''; var count = ''
while (this.exists(filename + count)) count = count - 1; while (this.exists(filename + count)) count = count - 1
this.rename(filename, filename + count); this.rename(filename, filename + count)
}
this.set(filename, content)
} }
this.set(filename, content);
};
this.sync = function () { this.sync = function () {
if (typeof chrome === 'undefined' || !chrome || !chrome.storage || !chrome.storage.sync) { if (typeof chrome === 'undefined' || !chrome || !chrome.storage || !chrome.storage.sync) {
return; return
} }
var obj = {}; var obj = {}
var done = false; var done = false
var count = 0; var count = 0
function check (key) { function check (key) {
chrome.storage.sync.get(key, function (resp) { chrome.storage.sync.get(key, function (resp) {
console.log('comparing to cloud', key, resp); console.log('comparing to cloud', key, resp)
if (typeof resp[key] !== 'undefined' && obj[key] !== resp[key] && confirm('Overwrite "' + utils.fileNameFromKey(key) + '"? Click Ok to overwrite local file with file from cloud. Cancel will push your local file to the cloud.')) { if (typeof resp[key] !== 'undefined' && obj[key] !== resp[key] && confirm('Overwrite "' + utils.fileNameFromKey(key) + '"? Click Ok to overwrite local file with file from cloud. Cancel will push your local file to the cloud.')) {
console.log('Overwriting', key); console.log('Overwriting', key)
window.localStorage.setItem(key, resp[key]); window.localStorage.setItem(key, resp[key])
updateFiles(); updateFiles()
} else { } else {
console.log('add to obj', obj, key); console.log('add to obj', obj, key)
obj[key] = window.localStorage[key]; obj[key] = window.localStorage[key]
} }
done++; done++
if (done >= count) { if (done >= count) {
chrome.storage.sync.set(obj, function () { chrome.storage.sync.set(obj, function () {
console.log('updated cloud files with: ', obj, this, arguments); console.log('updated cloud files with: ', obj, this, arguments)
}); })
} }
}); })
} }
for (var y in window.localStorage) { for (var y in window.localStorage) {
console.log('checking', y); console.log('checking', y)
obj[y] = window.localStorage.getItem(y); obj[y] = window.localStorage.getItem(y)
if (!utils.isCachedFile(y)) { if (!utils.isCachedFile(y)) {
continue; continue
}
count++
check(y)
} }
count++;
check(y);
} }
};
} }
module.exports = Storage; module.exports = Storage
'use strict'; 'use strict'
var $ = require('jquery'); var $ = require('jquery')
module.exports = { module.exports = {
tableRowItems: function (first, second, cls) { tableRowItems: function (first, second, cls) {
return $('<div class="crow"/>') return $('<div class="crow"/>')
.addClass(cls) .addClass(cls)
.append($('<div class="col1">').append(first)) .append($('<div class="col1">').append(first))
.append($('<div class="col2">').append(second)); .append($('<div class="col2">').append(second))
}, },
tableRow: function (description, data) { tableRow: function (description, data) {
return this.tableRowItems( return this.tableRowItems(
$('<span/>').text(description), $('<span/>').text(description),
$('<input readonly="readonly"/>').val(data)); $('<input readonly="readonly"/>').val(data))
}, },
textRow: function (description, data, cls) { textRow: function (description, data, cls) {
return this.tableRowItems( return this.tableRowItems(
$('<strong/>').text(description), $('<strong/>').text(description),
$('<textarea readonly="readonly" class="gethDeployText"/>').val(data), $('<textarea readonly="readonly" class="gethDeployText"/>').val(data),
cls); cls)
}, },
preRow: function (description, data) { preRow: function (description, data) {
return this.tableRowItems( return this.tableRowItems(
$('<span/>').text(description), $('<span/>').text(description),
$('<pre/>').text(data) $('<pre/>').text(data)
); )
}, },
formatAssemblyText: function (asm, prefix, source) { formatAssemblyText: function (asm, prefix, source) {
var self = this; var self = this
if (typeof asm === typeof '' || asm === null || asm === undefined) { if (typeof asm === typeof '' || asm === null || asm === undefined) {
return prefix + asm + '\n'; return prefix + asm + '\n'
} }
var text = prefix + '.code\n'; var text = prefix + '.code\n'
$.each(asm['.code'], function (i, item) { $.each(asm['.code'], function (i, item) {
var v = item.value === undefined ? '' : item.value; var v = item.value === undefined ? '' : item.value
var src = ''; var src = ''
if (item.begin !== undefined && item.end !== undefined) { if (item.begin !== undefined && item.end !== undefined) {
src = source.slice(item.begin, item.end).replace('\n', '\\n', 'g'); src = source.slice(item.begin, item.end).replace('\n', '\\n', 'g')
} }
if (src.length > 30) { if (src.length > 30) {
src = src.slice(0, 30) + '...'; src = src.slice(0, 30) + '...'
} }
if (item.name !== 'tag') { if (item.name !== 'tag') {
text += ' '; text += ' '
} }
text += prefix + item.name + ' ' + v + '\t\t\t' + src + '\n'; text += prefix + item.name + ' ' + v + '\t\t\t' + src + '\n'
}); })
text += prefix + '.data\n'; text += prefix + '.data\n'
if (asm['.data']) { if (asm['.data']) {
$.each(asm['.data'], function (i, item) { $.each(asm['.data'], function (i, item) {
text += ' ' + prefix + '' + i + ':\n'; text += ' ' + prefix + '' + i + ':\n'
text += self.formatAssemblyText(item, prefix + ' ', source); text += self.formatAssemblyText(item, prefix + ' ', source)
}); })
} }
return text; return text
}, },
gethDeploy: function (contractName, jsonInterface, bytecode) { gethDeploy: function (contractName, jsonInterface, bytecode) {
var code = ''; var code = ''
var funABI = this.getConstructorInterface(JSON.parse(jsonInterface)); var funABI = this.getConstructorInterface(JSON.parse(jsonInterface))
funABI.inputs.forEach(function (inp) { funABI.inputs.forEach(function (inp) {
code += 'var ' + inp.name + ' = /* var of type ' + inp.type + ' here */ ;\n'; code += 'var ' + inp.name + ' = /* var of type ' + inp.type + ' here */ ;\n'
}); })
code += 'var ' + contractName + 'Contract = web3.eth.contract(' + jsonInterface.replace('\n', '') + ');' + code += 'var ' + contractName + 'Contract = web3.eth.contract(' + jsonInterface.replace('\n', '') + ');' +
'\nvar ' + contractName + ' = ' + contractName + 'Contract.new('; '\nvar ' + contractName + ' = ' + contractName + 'Contract.new('
funABI.inputs.forEach(function (inp) { funABI.inputs.forEach(function (inp) {
code += '\n ' + inp.name + ','; code += '\n ' + inp.name + ','
}); })
code += '\n {' + code += '\n {' +
'\n from: web3.eth.accounts[0], ' + '\n from: web3.eth.accounts[0], ' +
...@@ -84,90 +84,90 @@ module.exports = { ...@@ -84,90 +84,90 @@ module.exports = {
"\n if (typeof contract.address !== 'undefined') {" + "\n if (typeof contract.address !== 'undefined') {" +
"\n console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);" + "\n console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);" +
'\n }' + '\n }' +
'\n })'; '\n })'
return code; return code
}, },
getConstructorInterface: function (abi) { getConstructorInterface: function (abi) {
var funABI = { 'name': '', 'inputs': [], 'type': 'constructor', 'outputs': [] }; var funABI = { 'name': '', 'inputs': [], 'type': 'constructor', 'outputs': [] }
for (var i = 0; i < abi.length; i++) { for (var i = 0; i < abi.length; i++) {
if (abi[i].type === 'constructor') { if (abi[i].type === 'constructor') {
funABI.inputs = abi[i].inputs || []; funABI.inputs = abi[i].inputs || []
break; break
} }
} }
return funABI; return funABI
}, },
formatGasEstimates: function (data) { formatGasEstimates: function (data) {
// FIXME: the whole gasEstimates object should be nil instead // FIXME: the whole gasEstimates object should be nil instead
if (data.creation === undefined && data.external === undefined && data.internal === undefined) { if (data.creation === undefined && data.external === undefined && data.internal === undefined) {
return; return
} }
var gasToText = function (g) { var gasToText = function (g) {
return g === null ? 'unknown' : g; return g === null ? 'unknown' : g
}; }
var text = ''; var text = ''
var fun; var fun
if ('creation' in data) { if ('creation' in data) {
text += 'Creation: ' + gasToText(data.creation[0]) + ' + ' + gasToText(data.creation[1]) + '\n'; text += 'Creation: ' + gasToText(data.creation[0]) + ' + ' + gasToText(data.creation[1]) + '\n'
} }
if ('external' in data) { if ('external' in data) {
text += 'External:\n'; text += 'External:\n'
for (fun in data.external) { for (fun in data.external) {
text += ' ' + fun + ': ' + gasToText(data.external[fun]) + '\n'; text += ' ' + fun + ': ' + gasToText(data.external[fun]) + '\n'
} }
} }
if ('internal' in data) { if ('internal' in data) {
text += 'Internal:\n'; text += 'Internal:\n'
for (fun in data.internal) { for (fun in data.internal) {
text += ' ' + fun + ': ' + gasToText(data.internal[fun]) + '\n'; text += ' ' + fun + ': ' + gasToText(data.internal[fun]) + '\n'
} }
} }
return text; return text
}, },
detailsOpen: {}, detailsOpen: {},
getDetails: function (contract, source, contractName) { getDetails: function (contract, source, contractName) {
var button = $('<button>Toggle Details</button>'); var button = $('<button>Toggle Details</button>')
var details = $('<div style="display: none;"/>'); var details = $('<div style="display: none;"/>')
var funHashes = ''; var funHashes = ''
for (var fun in contract.functionHashes) { for (var fun in contract.functionHashes) {
funHashes += contract.functionHashes[fun] + ' ' + fun + '\n'; funHashes += contract.functionHashes[fun] + ' ' + fun + '\n'
} }
details.append(this.preRow('Functions', funHashes)); details.append(this.preRow('Functions', funHashes))
var gasEstimates = this.formatGasEstimates(contract.gasEstimates); var gasEstimates = this.formatGasEstimates(contract.gasEstimates)
if (gasEstimates) { if (gasEstimates) {
details.append(this.preRow('Gas Estimates', gasEstimates)); details.append(this.preRow('Gas Estimates', gasEstimates))
} }
if (contract.runtimeBytecode && contract.runtimeBytecode.length > 0) { if (contract.runtimeBytecode && contract.runtimeBytecode.length > 0) {
details.append(this.tableRow('Runtime Bytecode', contract.runtimeBytecode)); details.append(this.tableRow('Runtime Bytecode', contract.runtimeBytecode))
} }
if (contract.opcodes !== undefined && contract.opcodes !== '') { if (contract.opcodes !== undefined && contract.opcodes !== '') {
details.append(this.tableRow('Opcodes', contract.opcodes)); details.append(this.tableRow('Opcodes', contract.opcodes))
} }
if (contract.assembly !== null) { if (contract.assembly !== null) {
details.append(this.preRow('Assembly', this.formatAssemblyText(contract.assembly, '', source))); details.append(this.preRow('Assembly', this.formatAssemblyText(contract.assembly, '', source)))
} }
var self = this; var self = this
button.click(function () { button.click(function () {
self.detailsOpen[contractName] = !self.detailsOpen[contractName]; self.detailsOpen[contractName] = !self.detailsOpen[contractName]
details.toggle(); details.toggle()
}); })
if (this.detailsOpen[contractName]) { if (this.detailsOpen[contractName]) {
details.show(); details.show()
} }
return $('<div class="contractDetails"/>').append(button).append(details); return $('<div class="contractDetails"/>').append(button).append(details)
} }
}; }
'use strict'; 'use strict'
var SOL_CACHE_FILE_PREFIX = 'sol-cache-file-'; var SOL_CACHE_FILE_PREFIX = 'sol-cache-file-'
function getCacheFilePrefix () { function getCacheFilePrefix () {
return SOL_CACHE_FILE_PREFIX; return SOL_CACHE_FILE_PREFIX
} }
function isCachedFile (name) { function isCachedFile (name) {
return name.indexOf(getCacheFilePrefix(), 0) === 0; return name.indexOf(getCacheFilePrefix(), 0) === 0
} }
function fileKey (name) { function fileKey (name) {
return getCacheFilePrefix() + name; return getCacheFilePrefix() + name
} }
function fileNameFromKey (key) { function fileNameFromKey (key) {
return key.replace(getCacheFilePrefix(), ''); return key.replace(getCacheFilePrefix(), '')
} }
function errortype (message) { function errortype (message) {
return message.match(/^.*:[0-9]*:[0-9]* Warning: /) ? 'warning' : 'error'; return message.match(/^.*:[0-9]*:[0-9]* Warning: /) ? 'warning' : 'error'
} }
module.exports = { module.exports = {
...@@ -27,4 +27,4 @@ module.exports = { ...@@ -27,4 +27,4 @@ module.exports = {
fileKey: fileKey, fileKey: fileKey,
fileNameFromKey: fileNameFromKey, fileNameFromKey: fileNameFromKey,
errortype: errortype errortype: errortype
}; }
'use strict'; 'use strict'
require('es6-shim'); require('es6-shim')
var app = require('./app.js'); var app = require('./app.js')
var $ = require('jquery'); var $ = require('jquery')
$(document).ready(function () { app.run(); }); $(document).ready(function () { app.run() })
'use strict'; 'use strict'
function eventManager () { function eventManager () {
this.registered = {}; this.registered = {}
} }
/* /*
...@@ -14,17 +14,17 @@ function eventManager () { ...@@ -14,17 +14,17 @@ function eventManager () {
*/ */
eventManager.prototype.unregister = function (eventName, obj, func) { eventManager.prototype.unregister = function (eventName, obj, func) {
if (obj instanceof Function) { if (obj instanceof Function) {
func = obj; func = obj
obj = {}; obj = {}
} }
for (var reg in this.registered[eventName]) { for (var reg in this.registered[eventName]) {
if (this.registered[eventName][reg] && if (this.registered[eventName][reg] &&
this.registered[eventName][reg].obj === obj && (!func || this.registered[eventName][reg].func === func)) { this.registered[eventName][reg].obj === obj && (!func || this.registered[eventName][reg].func === func)) {
this.registered[eventName].splice(reg, 1); this.registered[eventName].splice(reg, 1)
return; return
} }
} }
}; }
/* /*
* Register a new listenner. * Register a new listenner.
...@@ -36,17 +36,17 @@ eventManager.prototype.unregister = function (eventName, obj, func) { ...@@ -36,17 +36,17 @@ eventManager.prototype.unregister = function (eventName, obj, func) {
*/ */
eventManager.prototype.register = function (eventName, obj, func) { eventManager.prototype.register = function (eventName, obj, func) {
if (!this.registered[eventName]) { if (!this.registered[eventName]) {
this.registered[eventName] = []; this.registered[eventName] = []
} }
if (obj instanceof Function) { if (obj instanceof Function) {
func = obj; func = obj
obj = {}; obj = {}
} }
this.registered[eventName].push({ this.registered[eventName].push({
obj: obj, obj: obj,
func: func func: func
}); })
}; }
/* /*
* trigger event. * trigger event.
...@@ -57,9 +57,9 @@ eventManager.prototype.register = function (eventName, obj, func) { ...@@ -57,9 +57,9 @@ eventManager.prototype.register = function (eventName, obj, func) {
*/ */
eventManager.prototype.trigger = function (eventName, args) { eventManager.prototype.trigger = function (eventName, args) {
for (var listener in this.registered[eventName]) { for (var listener in this.registered[eventName]) {
var l = this.registered[eventName][listener]; var l = this.registered[eventName][listener]
l.func.apply(l.obj, args); l.func.apply(l.obj, args)
} }
}; }
module.exports = eventManager; module.exports = eventManager
/* global prompt */ /* global prompt */
'use strict'; 'use strict'
var $ = require('jquery'); var $ = require('jquery')
var ethJSUtil = require('ethereumjs-util'); var ethJSUtil = require('ethereumjs-util')
var EthJSTX = require('ethereumjs-tx'); var EthJSTX = require('ethereumjs-tx')
var ethJSABI = require('ethereumjs-abi'); var ethJSABI = require('ethereumjs-abi')
var EthJSBlock = require('ethereumjs-block'); var EthJSBlock = require('ethereumjs-block')
var BN = ethJSUtil.BN; var BN = ethJSUtil.BN
var EventManager = require('./lib/eventManager'); var EventManager = require('./lib/eventManager')
var crypto = require('crypto'); var crypto = require('crypto')
/* /*
trigger debugRequested trigger debugRequested
*/ */
function UniversalDApp (executionContext, options, txdebugger) { function UniversalDApp (executionContext, options, txdebugger) {
this.event = new EventManager(); this.event = new EventManager()
var self = this; var self = this
self.options = options || {}; self.options = options || {}
self.$el = $('<div class="udapp" />'); self.$el = $('<div class="udapp" />')
self.personalMode = self.options.personalMode || false; self.personalMode = self.options.personalMode || false
self.contracts; self.contracts
self.getAddress; self.getAddress
self.getValue; self.getValue
self.getGasLimit; self.getGasLimit
self.txdebugger = txdebugger; // temporary: will not be needed anymore when we'll add memory support to the VM self.txdebugger = txdebugger // temporary: will not be needed anymore when we'll add memory support to the VM
var defaultRenderOutputModifier = function (name, content) { return content; }; var defaultRenderOutputModifier = function (name, content) { return content }
self.renderOutputModifier = defaultRenderOutputModifier; self.renderOutputModifier = defaultRenderOutputModifier
self.web3 = executionContext.web3(); self.web3 = executionContext.web3()
self.vm = executionContext.vm(); self.vm = executionContext.vm()
self.executionContext = executionContext; self.executionContext = executionContext
self.executionContext.event.register('contextChanged', this, function (context) { self.executionContext.event.register('contextChanged', this, function (context) {
self.reset(self.contracts); self.reset(self.contracts)
}); })
} }
UniversalDApp.prototype.reset = function (contracts, getAddress, getValue, getGasLimit, renderer) { UniversalDApp.prototype.reset = function (contracts, getAddress, getValue, getGasLimit, renderer) {
this.$el.empty(); this.$el.empty()
this.contracts = contracts; this.contracts = contracts
this.getAddress = getAddress; this.getAddress = getAddress
this.getValue = getValue; this.getValue = getValue
this.getGasLimit = getGasLimit; this.getGasLimit = getGasLimit
this.renderOutputModifier = renderer; this.renderOutputModifier = renderer
this.accounts = {}; this.accounts = {}
if (this.executionContext.isVM()) { if (this.executionContext.isVM()) {
this._addAccount('3cd7232cd6f3fc66a57a6bedc1a8ed6c228fff0a327e169c2bcc5e869ed49511'); this._addAccount('3cd7232cd6f3fc66a57a6bedc1a8ed6c228fff0a327e169c2bcc5e869ed49511')
this._addAccount('2ac6c190b09897cd8987869cc7b918cfea07ee82038d492abce033c75c1b1d0c'); this._addAccount('2ac6c190b09897cd8987869cc7b918cfea07ee82038d492abce033c75c1b1d0c')
this._addAccount('dae9801649ba2d95a21e688b56f77905e5667c44ce868ec83f82e838712a2c7a'); this._addAccount('dae9801649ba2d95a21e688b56f77905e5667c44ce868ec83f82e838712a2c7a')
this._addAccount('d74aa6d18aa79a05f3473dd030a97d3305737cbc8337d940344345c1f6b72eea'); this._addAccount('d74aa6d18aa79a05f3473dd030a97d3305737cbc8337d940344345c1f6b72eea')
this._addAccount('71975fbf7fe448e004ac7ae54cad0a383c3906055a65468714156a07385e96ce'); this._addAccount('71975fbf7fe448e004ac7ae54cad0a383c3906055a65468714156a07385e96ce')
this.blockNumber = 1150000; // The VM is running in Homestead mode, which started at this block. this.blockNumber = 1150000 // The VM is running in Homestead mode, which started at this block.
} }
}; }
UniversalDApp.prototype.newAccount = function (password) { UniversalDApp.prototype.newAccount = function (password) {
if (!this.executionContext.isVM()) { if (!this.executionContext.isVM()) {
if (!this.personalMode) { if (!this.personalMode) {
throw new Error('Not running in personal mode'); throw new Error('Not running in personal mode')
} }
this.web3.personal.newAccount(password); this.web3.personal.newAccount(password)
} else { } else {
var privateKey; var privateKey
do { do {
privateKey = crypto.randomBytes(32); privateKey = crypto.randomBytes(32)
} while (!ethJSUtil.isValidPrivate(privateKey)); } while (!ethJSUtil.isValidPrivate(privateKey))
this._addAccount(privateKey); this._addAccount(privateKey)
} }
}; }
UniversalDApp.prototype._addAccount = function (privateKey, balance) { UniversalDApp.prototype._addAccount = function (privateKey, balance) {
var self = this; var self = this
if (!self.executionContext.isVM()) { if (!self.executionContext.isVM()) {
throw new Error('_addAccount() cannot be called in non-VM mode'); throw new Error('_addAccount() cannot be called in non-VM mode')
} }
if (self.accounts) { if (self.accounts) {
privateKey = new Buffer(privateKey, 'hex'); privateKey = new Buffer(privateKey, 'hex')
var address = ethJSUtil.privateToAddress(privateKey); var address = ethJSUtil.privateToAddress(privateKey)
// FIXME: we don't care about the callback, but we should still make this proper // FIXME: we don't care about the callback, but we should still make this proper
self.vm.stateManager.putAccountBalance(address, balance || 'f00000000000000001', function cb () {}); self.vm.stateManager.putAccountBalance(address, balance || 'f00000000000000001', function cb () {})
self.accounts['0x' + address.toString('hex')] = { privateKey: privateKey, nonce: 0 }; self.accounts['0x' + address.toString('hex')] = { privateKey: privateKey, nonce: 0 }
} }
}; }
UniversalDApp.prototype.getAccounts = function (cb) { UniversalDApp.prototype.getAccounts = function (cb) {
var self = this; var self = this
if (!self.executionContext.isVM()) { if (!self.executionContext.isVM()) {
// Weirdness of web3: listAccounts() is sync, `getListAccounts()` is async // Weirdness of web3: listAccounts() is sync, `getListAccounts()` is async
// See: https://github.com/ethereum/web3.js/issues/442 // See: https://github.com/ethereum/web3.js/issues/442
if (self.personalMode) { if (self.personalMode) {
self.web3.personal.getListAccounts(cb); self.web3.personal.getListAccounts(cb)
} else { } else {
self.web3.eth.getAccounts(cb); self.web3.eth.getAccounts(cb)
} }
} else { } else {
if (!self.accounts) { if (!self.accounts) {
return cb('No accounts?'); return cb('No accounts?')
} }
cb(null, Object.keys(self.accounts)); cb(null, Object.keys(self.accounts))
} }
}; }
UniversalDApp.prototype.getBalance = function (address, cb) { UniversalDApp.prototype.getBalance = function (address, cb) {
var self = this; var self = this
address = ethJSUtil.stripHexPrefix(address); address = ethJSUtil.stripHexPrefix(address)
if (!self.executionContext.isVM()) { if (!self.executionContext.isVM()) {
self.web3.eth.getBalance(address, function (err, res) { self.web3.eth.getBalance(address, function (err, res) {
if (err) { if (err) {
cb(err); cb(err)
} else { } else {
cb(null, res.toString(10)); cb(null, res.toString(10))
} }
}); })
} else { } else {
if (!self.accounts) { if (!self.accounts) {
return cb('No accounts?'); return cb('No accounts?')
} }
self.vm.stateManager.getAccountBalance(new Buffer(address, 'hex'), function (err, res) { self.vm.stateManager.getAccountBalance(new Buffer(address, 'hex'), function (err, res) {
if (err) { if (err) {
cb('Account not found'); cb('Account not found')
} else { } else {
cb(null, new BN(res).toString(10)); cb(null, new BN(res).toString(10))
} }
}); })
} }
}; }
UniversalDApp.prototype.render = function () { UniversalDApp.prototype.render = function () {
var self = this; var self = this
// NOTE: don't display anything if there are no contracts to display // NOTE: don't display anything if there are no contracts to display
if (self.contracts.length === 0) { if (self.contracts.length === 0) {
return self.$el; return self.$el
} }
var $legend = $('<div class="legend" />') var $legend = $('<div class="legend" />')
.append($('<div class="attach"/>').text('Attach')) .append($('<div class="attach"/>').text('Attach'))
.append($('<div class="transact"/>').text('Transact')) .append($('<div class="transact"/>').text('Transact'))
.append($('<div class="call"/>').text('Call')); .append($('<div class="call"/>').text('Call'))
self.$el.append($legend); self.$el.append($legend)
for (var c in self.contracts) { for (var c in self.contracts) {
var $contractEl = $('<div class="contract"/>'); var $contractEl = $('<div class="contract"/>')
if (self.contracts[c].address) { if (self.contracts[c].address) {
self.getInstanceInterface(self.contracts[c], self.contracts[c].address, $contractEl); self.getInstanceInterface(self.contracts[c], self.contracts[c].address, $contractEl)
} else { } else {
var $title = $('<span class="title"/>').text(self.contracts[c].name); var $title = $('<span class="title"/>').text(self.contracts[c].name)
if (self.contracts[c].bytecode) { if (self.contracts[c].bytecode) {
$title.append($('<div class="size"/>').text((self.contracts[c].bytecode.length / 2) + ' bytes')); $title.append($('<div class="size"/>').text((self.contracts[c].bytecode.length / 2) + ' bytes'))
} }
$contractEl.append($title).append(self.getCreateInterface($contractEl, self.contracts[c])); $contractEl.append($title).append(self.getCreateInterface($contractEl, self.contracts[c]))
} }
self.$el.append(self.renderOutputModifier(self.contracts[c].name, $contractEl)); self.$el.append(self.renderOutputModifier(self.contracts[c].name, $contractEl))
} }
return self.$el; return self.$el
}; }
UniversalDApp.prototype.getContractByName = function (contractName) { UniversalDApp.prototype.getContractByName = function (contractName) {
var self = this; var self = this
for (var c in self.contracts) { for (var c in self.contracts) {
if (self.contracts[c].name === contractName) { if (self.contracts[c].name === contractName) {
return self.contracts[c]; return self.contracts[c]
} }
} }
return null; return null
}; }
UniversalDApp.prototype.getCreateInterface = function ($container, contract) { UniversalDApp.prototype.getCreateInterface = function ($container, contract) {
var self = this; var self = this
var $createInterface = $('<div class="create"/>'); var $createInterface = $('<div class="create"/>')
if (self.options.removable) { if (self.options.removable) {
var $close = $('<div class="udapp-close" />'); var $close = $('<div class="udapp-close" />')
$close.click(function () { self.$el.remove(); }); $close.click(function () { self.$el.remove() })
$createInterface.append($close); $createInterface.append($close)
} }
var $atButton = $('<button class="atAddress"/>').text('At Address').click(function () { self.clickContractAt(self, $container.find('.createContract'), contract); }); var $atButton = $('<button class="atAddress"/>').text('At Address').click(function () { self.clickContractAt(self, $container.find('.createContract'), contract) })
$createInterface.append($atButton); $createInterface.append($atButton)
var $newButton = self.getInstanceInterface(contract); var $newButton = self.getInstanceInterface(contract)
$createInterface.append($newButton); $createInterface.append($newButton)
// Only display creation interface for non-abstract contracts. // Only display creation interface for non-abstract contracts.
// FIXME: maybe have a flag for this in the JSON? // FIXME: maybe have a flag for this in the JSON?
// FIXME: maybe fix getInstanceInterface() below for this case // FIXME: maybe fix getInstanceInterface() below for this case
if (contract.bytecode.length === 0) { if (contract.bytecode.length === 0) {
var $createButton = $newButton.find('.constructor .call'); var $createButton = $newButton.find('.constructor .call')
// NOTE: we must show the button to have CSS properly lined up // NOTE: we must show the button to have CSS properly lined up
$createButton.text('Create'); $createButton.text('Create')
$createButton.attr('disabled', 'disabled'); $createButton.attr('disabled', 'disabled')
$createButton.attr('title', 'This contract does not implement all functions and thus cannot be created.'); $createButton.attr('title', 'This contract does not implement all functions and thus cannot be created.')
} }
return $createInterface; return $createInterface
}; }
UniversalDApp.prototype.getInstanceInterface = function (contract, address, $target) { UniversalDApp.prototype.getInstanceInterface = function (contract, address, $target) {
var self = this; var self = this
var abi = JSON.parse(contract.interface).sort(function (a, b) { var abi = JSON.parse(contract.interface).sort(function (a, b) {
if (a.name > b.name) { if (a.name > b.name) {
return -1; return -1
} else { } else {
return 1; return 1
} }
}).sort(function (a, b) { }).sort(function (a, b) {
if (a.constant === true) { if (a.constant === true) {
return -1; return -1
} else { } else {
return 1; return 1
} }
}); })
var funABI = self.getConstructorInterface(abi); var funABI = self.getConstructorInterface(abi)
var $createInterface = $('<div class="createContract"/>'); var $createInterface = $('<div class="createContract"/>')
var appendFunctions = function (address, $el) { var appendFunctions = function (address, $el) {
var $instance = $('<div class="instance"/>'); var $instance = $('<div class="instance"/>')
if (self.options.removable_instances) { if (self.options.removable_instances) {
var $close = $('<div class="udapp-close" />'); var $close = $('<div class="udapp-close" />')
$close.click(function () { $instance.remove(); }); $close.click(function () { $instance.remove() })
$instance.append($close); $instance.append($close)
} }
var context = self.executionContext.isVM() ? 'memory' : 'blockchain'; var context = self.executionContext.isVM() ? 'memory' : 'blockchain'
address = (address.slice(0, 2) === '0x' ? '' : '0x') + address.toString('hex'); address = (address.slice(0, 2) === '0x' ? '' : '0x') + address.toString('hex')
var $title = $('<span class="title"/>').text(contract.name + ' at ' + address + ' (' + context + ')'); var $title = $('<span class="title"/>').text(contract.name + ' at ' + address + ' (' + context + ')')
$title.click(function () { $title.click(function () {
$instance.toggleClass('hide'); $instance.toggleClass('hide')
}); })
var $events = $('<div class="events"/>'); var $events = $('<div class="events"/>')
var parseLogs = function (err, response) { var parseLogs = function (err, response) {
if (err) { if (err) {
return; return
} }
var $event = $('<div class="event" />'); var $event = $('<div class="event" />')
var $close = $('<div class="udapp-close" />'); var $close = $('<div class="udapp-close" />')
$close.click(function () { $event.remove(); }); $close.click(function () { $event.remove() })
$event.append($('<span class="name"/>').text(response.event)) $event.append($('<span class="name"/>').text(response.event))
.append($('<span class="args" />').text(JSON.stringify(response.args, null, 2))) .append($('<span class="args" />').text(JSON.stringify(response.args, null, 2)))
.append($close); .append($close)
$events.append($event); $events.append($event)
}; }
if (self.executionContext.isVM()) { if (self.executionContext.isVM()) {
// FIXME: support indexed events // FIXME: support indexed events
var eventABI = {}; var eventABI = {}
$.each(abi, function (i, funABI) { $.each(abi, function (i, funABI) {
if (funABI.type !== 'event') { if (funABI.type !== 'event') {
return; return
} }
var hash = ethJSABI.eventID(funABI.name, funABI.inputs.map(function (item) { return item.type; })); var hash = ethJSABI.eventID(funABI.name, funABI.inputs.map(function (item) { return item.type }))
eventABI[hash.toString('hex')] = { event: funABI.name, inputs: funABI.inputs }; eventABI[hash.toString('hex')] = { event: funABI.name, inputs: funABI.inputs }
}); })
self.vm.on('afterTx', function (response) { self.vm.on('afterTx', function (response) {
for (var i in response.vm.logs) { for (var i in response.vm.logs) {
// [address, topics, mem] // [address, topics, mem]
var log = response.vm.logs[i]; var log = response.vm.logs[i]
var event; var event
var decoded; var decoded
try { try {
var abi = eventABI[log[1][0].toString('hex')]; var abi = eventABI[log[1][0].toString('hex')]
event = abi.event; event = abi.event
var types = abi.inputs.map(function (item) { var types = abi.inputs.map(function (item) {
return item.type; return item.type
}); })
decoded = ethJSABI.rawDecode(types, log[2]); decoded = ethJSABI.rawDecode(types, log[2])
decoded = ethJSABI.stringify(types, decoded); decoded = ethJSABI.stringify(types, decoded)
} catch (e) { } catch (e) {
decoded = '0x' + log[2].toString('hex'); decoded = '0x' + log[2].toString('hex')
} }
parseLogs(null, { event: event, args: decoded }); parseLogs(null, { event: event, args: decoded })
} }
}); })
} else { } else {
var eventFilter = self.web3.eth.contract(abi).at(address).allEvents(); var eventFilter = self.web3.eth.contract(abi).at(address).allEvents()
eventFilter.watch(parseLogs); eventFilter.watch(parseLogs)
} }
$instance.append($title); $instance.append($title)
// Add the fallback function // Add the fallback function
$instance.append(self.getCallButton({ $instance.append(self.getCallButton({
abi: { constant: false, inputs: [], name: '(fallback)', outputs: [], type: 'function' }, abi: { constant: false, inputs: [], name: '(fallback)', outputs: [], type: 'function' },
encode: function (args) { encode: function (args) {
return ''; return ''
}, },
address: address address: address
})); }))
$.each(abi, function (i, funABI) { $.each(abi, function (i, funABI) {
if (funABI.type !== 'function') { if (funABI.type !== 'function') {
return; return
} }
// @todo getData cannot be used with overloaded functions // @todo getData cannot be used with overloaded functions
$instance.append(self.getCallButton({ $instance.append(self.getCallButton({
abi: funABI, abi: funABI,
encode: function (args) { encode: function (args) {
var types = []; var types = []
for (var i = 0; i < funABI.inputs.length; i++) { for (var i = 0; i < funABI.inputs.length; i++) {
types.push(funABI.inputs[i].type); types.push(funABI.inputs[i].type)
} }
return Buffer.concat([ ethJSABI.methodID(funABI.name, types), ethJSABI.rawEncode(types, args) ]).toString('hex'); return Buffer.concat([ ethJSABI.methodID(funABI.name, types), ethJSABI.rawEncode(types, args) ]).toString('hex')
}, },
address: address address: address
})); }))
}); })
($el || $createInterface).append($instance.append($events)); ($el || $createInterface).append($instance.append($events))
}; }
if (!address || !$target) { if (!address || !$target) {
$createInterface.append(self.getCallButton({ $createInterface.append(self.getCallButton({
abi: funABI, abi: funABI,
encode: function (args) { encode: function (args) {
var types = []; var types = []
for (var i = 0; i < funABI.inputs.length; i++) { for (var i = 0; i < funABI.inputs.length; i++) {
types.push(funABI.inputs[i].type); types.push(funABI.inputs[i].type)
} }
// NOTE: the caller will concatenate the bytecode and this // NOTE: the caller will concatenate the bytecode and this
// it could be done here too for consistency // it could be done here too for consistency
return ethJSABI.rawEncode(types, args).toString('hex'); return ethJSABI.rawEncode(types, args).toString('hex')
}, },
contractName: contract.name, contractName: contract.name,
bytecode: contract.bytecode, bytecode: contract.bytecode,
appendFunctions: appendFunctions appendFunctions: appendFunctions
})); }))
} else { } else {
appendFunctions(address, $target); appendFunctions(address, $target)
} }
return $createInterface; return $createInterface
}; }
// either return the supplied constructor or a mockup (we assume everything can be instantiated) // either return the supplied constructor or a mockup (we assume everything can be instantiated)
UniversalDApp.prototype.getConstructorInterface = function (abi) { UniversalDApp.prototype.getConstructorInterface = function (abi) {
var funABI = { 'name': '', 'inputs': [], 'type': 'constructor', 'outputs': [] }; var funABI = { 'name': '', 'inputs': [], 'type': 'constructor', 'outputs': [] }
for (var i = 0; i < abi.length; i++) { for (var i = 0; i < abi.length; i++) {
if (abi[i].type === 'constructor') { if (abi[i].type === 'constructor') {
funABI.inputs = abi[i].inputs || []; funABI.inputs = abi[i].inputs || []
break; break
} }
} }
return funABI; return funABI
}; }
UniversalDApp.prototype.getCallButton = function (args) { UniversalDApp.prototype.getCallButton = function (args) {
var self = this; var self = this
// args.abi, args.encode, args.bytecode [constr only], args.address [fun only] // args.abi, args.encode, args.bytecode [constr only], args.address [fun only]
// args.contractName [constr only], args.appendFunctions [constr only] // args.contractName [constr only], args.appendFunctions [constr only]
var isConstructor = args.bytecode !== undefined; var isConstructor = args.bytecode !== undefined
var lookupOnly = (args.abi.constant && !isConstructor); var lookupOnly = (args.abi.constant && !isConstructor)
var inputs = ''; var inputs = ''
$.each(args.abi.inputs, function (i, inp) { $.each(args.abi.inputs, function (i, inp) {
if (inputs !== '') { if (inputs !== '') {
inputs += ', '; inputs += ', '
} }
inputs += inp.type + ' ' + inp.name; inputs += inp.type + ' ' + inp.name
}); })
var inputField = $('<input/>').attr('placeholder', inputs).attr('title', inputs); var inputField = $('<input/>').attr('placeholder', inputs).attr('title', inputs)
var $outputOverride = $('<div class="value" />'); var $outputOverride = $('<div class="value" />')
var outputSpan = $('<div class="output"/>'); var outputSpan = $('<div class="output"/>')
var getReturnOutput = function (result) { var getReturnOutput = function (result) {
var returnName = lookupOnly ? 'Value' : 'Result'; var returnName = lookupOnly ? 'Value' : 'Result'
var returnCls = lookupOnly ? 'value' : 'returned'; var returnCls = lookupOnly ? 'value' : 'returned'
return $('<div class="' + returnCls + '">').html('<strong>' + returnName + ':</strong> ' + JSON.stringify(result, null, 2)); return $('<div class="' + returnCls + '">').html('<strong>' + returnName + ':</strong> ' + JSON.stringify(result, null, 2))
}; }
var getDebugTransaction = function (result) { var getDebugTransaction = function (result) {
var $debugTx = $('<div class="debugTx">'); var $debugTx = $('<div class="debugTx">')
var $button = $('<button title="Launch Debugger" class="debug"><i class="fa fa-bug"></i></button>'); var $button = $('<button title="Launch Debugger" class="debug"><i class="fa fa-bug"></i></button>')
$button.click(function () { $button.click(function () {
self.event.trigger('debugRequested', [result]); self.event.trigger('debugRequested', [result])
}); })
$debugTx.append($button); $debugTx.append($button)
return $debugTx; return $debugTx
}; }
var getDebugCall = function (result) { var getDebugCall = function (result) {
var $debugTx = $('<div class="debugCall">'); var $debugTx = $('<div class="debugCall">')
var $button = $('<button title="Launch Debugger" class="debug"><i class="fa fa-bug"></i></button>'); var $button = $('<button title="Launch Debugger" class="debug"><i class="fa fa-bug"></i></button>')
$button.click(function () { $button.click(function () {
self.event.trigger('debugRequested', [result]); self.event.trigger('debugRequested', [result])
}); })
$debugTx.append($button); $debugTx.append($button)
return $debugTx; return $debugTx
}; }
var getGasUsedOutput = function (result, vmResult) { var getGasUsedOutput = function (result, vmResult) {
var $gasUsed = $('<div class="gasUsed">'); var $gasUsed = $('<div class="gasUsed">')
var caveat = lookupOnly ? '<em>(<span class="caveat" title="Cost only applies when called by a contract">caveat</span>)</em>' : ''; var caveat = lookupOnly ? '<em>(<span class="caveat" title="Cost only applies when called by a contract">caveat</span>)</em>' : ''
var gas; var gas
if (result.gasUsed) { if (result.gasUsed) {
gas = result.gasUsed.toString(10); gas = result.gasUsed.toString(10)
$gasUsed.html('<strong>Transaction cost:</strong> ' + gas + ' gas. ' + caveat); $gasUsed.html('<strong>Transaction cost:</strong> ' + gas + ' gas. ' + caveat)
} }
if (vmResult && vmResult.gasUsed) { if (vmResult && vmResult.gasUsed) {
var $callGasUsed = $('<div class="gasUsed">'); var $callGasUsed = $('<div class="gasUsed">')
gas = vmResult.gasUsed.toString(10); gas = vmResult.gasUsed.toString(10)
$callGasUsed.append('<strong>Execution cost:</strong> ' + gas + ' gas.'); $callGasUsed.append('<strong>Execution cost:</strong> ' + gas + ' gas.')
$gasUsed.append($callGasUsed); $gasUsed.append($callGasUsed)
}
return $gasUsed
} }
return $gasUsed;
};
var getDecodedOutput = function (result) { var getDecodedOutput = function (result) {
var $decoded; var $decoded
if (Array.isArray(result)) { if (Array.isArray(result)) {
$decoded = $('<ol>'); $decoded = $('<ol>')
for (var i = 0; i < result.length; i++) { for (var i = 0; i < result.length; i++) {
$decoded.append($('<li>').text(result[i])); $decoded.append($('<li>').text(result[i]))
} }
} else { } else {
$decoded = result; $decoded = result
}
return $('<div class="decoded">').html('<strong>Decoded:</strong> ').append($decoded)
} }
return $('<div class="decoded">').html('<strong>Decoded:</strong> ').append($decoded);
};
var getOutput = function () { var getOutput = function () {
var $result = $('<div class="result" />'); var $result = $('<div class="result" />')
var $close = $('<div class="udapp-close" />'); var $close = $('<div class="udapp-close" />')
$close.click(function () { $result.remove(); }); $close.click(function () { $result.remove() })
$result.append($close); $result.append($close)
return $result; return $result
}; }
var clearOutput = function ($result) { var clearOutput = function ($result) {
$(':not(.udapp-close)', $result).remove(); $(':not(.udapp-close)', $result).remove()
}; }
var replaceOutput = function ($result, message) { var replaceOutput = function ($result, message) {
clearOutput($result); clearOutput($result)
$result.append(message); $result.append(message)
}; }
var handleCallButtonClick = function (ev, $result) { var handleCallButtonClick = function (ev, $result) {
if (!$result) { if (!$result) {
$result = getOutput(); $result = getOutput()
if (lookupOnly && !inputs.length) { if (lookupOnly && !inputs.length) {
$outputOverride.empty().append($result); $outputOverride.empty().append($result)
} else { } else {
outputSpan.append($result); outputSpan.append($result)
} }
} }
var funArgs = ''; var funArgs = ''
try { try {
funArgs = $.parseJSON('[' + inputField.val() + ']'); funArgs = $.parseJSON('[' + inputField.val() + ']')
} catch (e) { } catch (e) {
replaceOutput($result, $('<span/>').text('Error encoding arguments: ' + e)); replaceOutput($result, $('<span/>').text('Error encoding arguments: ' + e))
return; return
} }
var data = ''; var data = ''
if (!isConstructor || funArgs.length > 0) { if (!isConstructor || funArgs.length > 0) {
try { try {
data = args.encode(funArgs); data = args.encode(funArgs)
} catch (e) { } catch (e) {
replaceOutput($result, $('<span/>').text('Error encoding arguments: ' + e)); replaceOutput($result, $('<span/>').text('Error encoding arguments: ' + e))
return; return
} }
} }
if (data.slice(0, 9) === 'undefined') { if (data.slice(0, 9) === 'undefined') {
data = data.slice(9); data = data.slice(9)
} }
if (data.slice(0, 2) === '0x') { if (data.slice(0, 2) === '0x') {
data = data.slice(2); data = data.slice(2)
} }
replaceOutput($result, $('<span>Waiting for transaction to be mined...</span>')); replaceOutput($result, $('<span>Waiting for transaction to be mined...</span>'))
if (isConstructor) { if (isConstructor) {
if (args.bytecode.indexOf('_') >= 0) { if (args.bytecode.indexOf('_') >= 0) {
replaceOutput($result, $('<span>Deploying and linking required libraries...</span>')); replaceOutput($result, $('<span>Deploying and linking required libraries...</span>'))
self.linkBytecode(args.contractName, function (err, bytecode) { self.linkBytecode(args.contractName, function (err, bytecode) {
if (err) { if (err) {
replaceOutput($result, $('<span/>').text('Error deploying required libraries: ' + err)); replaceOutput($result, $('<span/>').text('Error deploying required libraries: ' + err))
} else { } else {
args.bytecode = bytecode; args.bytecode = bytecode
handleCallButtonClick(ev, $result); handleCallButtonClick(ev, $result)
} }
}); })
return; return
} else { } else {
data = args.bytecode + data; data = args.bytecode + data
} }
} }
...@@ -510,235 +510,235 @@ UniversalDApp.prototype.getCallButton = function (args) { ...@@ -510,235 +510,235 @@ UniversalDApp.prototype.getCallButton = function (args) {
// Only decode if there supposed to be fields // Only decode if there supposed to be fields
if (args.abi.outputs.length > 0) { if (args.abi.outputs.length > 0) {
try { try {
var i; var i
var outputTypes = []; var outputTypes = []
for (i = 0; i < args.abi.outputs.length; i++) { for (i = 0; i < args.abi.outputs.length; i++) {
outputTypes.push(args.abi.outputs[i].type); outputTypes.push(args.abi.outputs[i].type)
} }
// decode data // decode data
var decodedObj = ethJSABI.rawDecode(outputTypes, response); var decodedObj = ethJSABI.rawDecode(outputTypes, response)
// format decoded data // format decoded data
decodedObj = ethJSABI.stringify(outputTypes, decodedObj); decodedObj = ethJSABI.stringify(outputTypes, decodedObj)
for (i = 0; i < outputTypes.length; i++) { for (i = 0; i < outputTypes.length; i++) {
var name = args.abi.outputs[i].name; var name = args.abi.outputs[i].name
if (name.length > 0) { if (name.length > 0) {
decodedObj[i] = outputTypes[i] + ' ' + name + ': ' + decodedObj[i]; decodedObj[i] = outputTypes[i] + ' ' + name + ': ' + decodedObj[i]
} else { } else {
decodedObj[i] = outputTypes[i] + ': ' + decodedObj[i]; decodedObj[i] = outputTypes[i] + ': ' + decodedObj[i]
} }
} }
return getDecodedOutput(decodedObj); return getDecodedOutput(decodedObj)
} catch (e) { } catch (e) {
return getDecodedOutput('Failed to decode output: ' + e); return getDecodedOutput('Failed to decode output: ' + e)
}
} }
} }
};
var decoded; var decoded
self.runTx({ to: args.address, data: data, useCall: args.abi.constant && !isConstructor }, function (err, result) { self.runTx({ to: args.address, data: data, useCall: args.abi.constant && !isConstructor }, function (err, result) {
if (err) { if (err) {
replaceOutput($result, $('<span/>').text(err).addClass('error')); replaceOutput($result, $('<span/>').text(err).addClass('error'))
// VM only // VM only
} else if (self.executionContext.isVM() && result.vm.exception === 0 && result.vm.exceptionError) { } else if (self.executionContext.isVM() && result.vm.exception === 0 && result.vm.exceptionError) {
replaceOutput($result, $('<span/>').text('VM Exception: ' + result.vm.exceptionError).addClass('error')); replaceOutput($result, $('<span/>').text('VM Exception: ' + result.vm.exceptionError).addClass('error'))
$result.append(getDebugTransaction(result)); $result.append(getDebugTransaction(result))
// VM only // VM only
} else if (self.executionContext.isVM() && result.vm.return === undefined) { } else if (self.executionContext.isVM() && result.vm.return === undefined) {
replaceOutput($result, $('<span/>').text('Exception during execution.').addClass('error')); replaceOutput($result, $('<span/>').text('Exception during execution.').addClass('error'))
$result.append(getDebugTransaction(result)); $result.append(getDebugTransaction(result))
} else if (isConstructor) { } else if (isConstructor) {
replaceOutput($result, getGasUsedOutput(result, result.vm)); replaceOutput($result, getGasUsedOutput(result, result.vm))
$result.append(getDebugTransaction(result)); $result.append(getDebugTransaction(result))
args.appendFunctions(self.executionContext.isVM() ? result.createdAddress : result.contractAddress); args.appendFunctions(self.executionContext.isVM() ? result.createdAddress : result.contractAddress)
} else if (self.executionContext.isVM()) { } else if (self.executionContext.isVM()) {
var outputObj = '0x' + result.vm.return.toString('hex'); var outputObj = '0x' + result.vm.return.toString('hex')
clearOutput($result); clearOutput($result)
$result.append(getReturnOutput(outputObj)).append(getGasUsedOutput(result, result.vm)); $result.append(getReturnOutput(outputObj)).append(getGasUsedOutput(result, result.vm))
decoded = decodeResponse(result.vm.return); decoded = decodeResponse(result.vm.return)
if (decoded) { if (decoded) {
$result.append(decoded); $result.append(decoded)
} }
if (args.abi.constant) { if (args.abi.constant) {
$result.append(getDebugCall(result)); $result.append(getDebugCall(result))
} else { } else {
$result.append(getDebugTransaction(result)); $result.append(getDebugTransaction(result))
} }
} else if (args.abi.constant && !isConstructor) { } else if (args.abi.constant && !isConstructor) {
clearOutput($result); clearOutput($result)
$result.append(getReturnOutput(result)).append(getGasUsedOutput({})); $result.append(getReturnOutput(result)).append(getGasUsedOutput({}))
decoded = decodeResponse(ethJSUtil.toBuffer(result)); decoded = decodeResponse(ethJSUtil.toBuffer(result))
if (decoded) { if (decoded) {
$result.append(decoded); $result.append(decoded)
} }
} else { } else {
clearOutput($result); clearOutput($result)
$result.append(getReturnOutput(result)).append(getGasUsedOutput(result)); $result.append(getReturnOutput(result)).append(getGasUsedOutput(result))
$result.append(getDebugTransaction(result)); $result.append(getDebugTransaction(result))
}
})
} }
});
};
var button = $('<button />') var button = $('<button />')
.addClass('call') .addClass('call')
.attr('title', args.abi.name) .attr('title', args.abi.name)
.text(args.bytecode ? 'Create' : args.abi.name) .text(args.bytecode ? 'Create' : args.abi.name)
.click(handleCallButtonClick); .click(handleCallButtonClick)
if (lookupOnly && !inputs.length) { if (lookupOnly && !inputs.length) {
handleCallButtonClick(); handleCallButtonClick()
} }
var $contractProperty = $('<div class="contractProperty"/>'); var $contractProperty = $('<div class="contractProperty"/>')
$contractProperty $contractProperty
.toggleClass('constant', !isConstructor && args.abi.constant) .toggleClass('constant', !isConstructor && args.abi.constant)
.toggleClass('hasArgs', args.abi.inputs.length > 0) .toggleClass('hasArgs', args.abi.inputs.length > 0)
.toggleClass('constructor', isConstructor) .toggleClass('constructor', isConstructor)
.append(button) .append(button)
.append((lookupOnly && !inputs.length) ? $outputOverride : inputField); .append((lookupOnly && !inputs.length) ? $outputOverride : inputField)
return $contractProperty.append(outputSpan); return $contractProperty.append(outputSpan)
}; }
UniversalDApp.prototype.linkBytecode = function (contractName, cb) { UniversalDApp.prototype.linkBytecode = function (contractName, cb) {
var self = this; var self = this
var bytecode = self.getContractByName(contractName).bytecode; var bytecode = self.getContractByName(contractName).bytecode
if (bytecode.indexOf('_') < 0) { if (bytecode.indexOf('_') < 0) {
return cb(null, bytecode); return cb(null, bytecode)
} }
var m = bytecode.match(/__([^_]{1,36})__/); var m = bytecode.match(/__([^_]{1,36})__/)
if (!m) { if (!m) {
return cb('Invalid bytecode format.'); return cb('Invalid bytecode format.')
} }
var libraryName = m[1]; var libraryName = m[1]
if (!self.getContractByName(libraryName)) { if (!self.getContractByName(libraryName)) {
return cb('Library ' + libraryName + ' not found.'); return cb('Library ' + libraryName + ' not found.')
} }
self.deployLibrary(libraryName, function (err, address) { self.deployLibrary(libraryName, function (err, address) {
if (err) { if (err) {
return cb(err); return cb(err)
} }
var libLabel = '__' + libraryName + Array(39 - libraryName.length).join('_'); var libLabel = '__' + libraryName + Array(39 - libraryName.length).join('_')
var hexAddress = address.toString('hex'); var hexAddress = address.toString('hex')
if (hexAddress.slice(0, 2) === '0x') { if (hexAddress.slice(0, 2) === '0x') {
hexAddress = hexAddress.slice(2); hexAddress = hexAddress.slice(2)
} }
hexAddress = Array(40 - hexAddress.length + 1).join('0') + hexAddress; hexAddress = Array(40 - hexAddress.length + 1).join('0') + hexAddress
while (bytecode.indexOf(libLabel) >= 0) { while (bytecode.indexOf(libLabel) >= 0) {
bytecode = bytecode.replace(libLabel, hexAddress); bytecode = bytecode.replace(libLabel, hexAddress)
} }
self.getContractByName(contractName).bytecode = bytecode; self.getContractByName(contractName).bytecode = bytecode
self.linkBytecode(contractName, cb); self.linkBytecode(contractName, cb)
}); })
}; }
UniversalDApp.prototype.deployLibrary = function (contractName, cb) { UniversalDApp.prototype.deployLibrary = function (contractName, cb) {
var self = this; var self = this
if (self.getContractByName(contractName).address) { if (self.getContractByName(contractName).address) {
return cb(null, self.getContractByName(contractName).address); return cb(null, self.getContractByName(contractName).address)
} }
var bytecode = self.getContractByName(contractName).bytecode; var bytecode = self.getContractByName(contractName).bytecode
if (bytecode.indexOf('_') >= 0) { if (bytecode.indexOf('_') >= 0) {
self.linkBytecode(contractName, function (err, bytecode) { self.linkBytecode(contractName, function (err, bytecode) {
if (err) cb(err); if (err) cb(err)
else self.deployLibrary(contractName, cb); else self.deployLibrary(contractName, cb)
}); })
} else { } else {
self.runTx({ data: bytecode, useCall: false }, function (err, result) { self.runTx({ data: bytecode, useCall: false }, function (err, result) {
if (err) { if (err) {
return cb(err); return cb(err)
} }
var address = self.executionContext.isVM() ? result.createdAddress : result.contractAddress; var address = self.executionContext.isVM() ? result.createdAddress : result.contractAddress
self.getContractByName(contractName).address = address; self.getContractByName(contractName).address = address
cb(err, address); cb(err, address)
}); })
} }
}; }
UniversalDApp.prototype.clickContractAt = function (self, $output, contract) { UniversalDApp.prototype.clickContractAt = function (self, $output, contract) {
var address = prompt('What Address is this contract at in the Blockchain? ie: 0xdeadbeaf...'); var address = prompt('What Address is this contract at in the Blockchain? ie: 0xdeadbeaf...')
self.getInstanceInterface(contract, address, $output); self.getInstanceInterface(contract, address, $output)
}; }
function tryTillResponse (web3, txhash, done) { function tryTillResponse (web3, txhash, done) {
web3.eth.getTransactionReceipt(txhash, function (err, address) { web3.eth.getTransactionReceipt(txhash, function (err, address) {
if (!err && !address) { if (!err && !address) {
// Try again with a bit of delay // Try again with a bit of delay
setTimeout(function () { tryTillResponse(web3, txhash, done); }, 500); setTimeout(function () { tryTillResponse(web3, txhash, done) }, 500)
} else { } else {
done(err, address); done(err, address)
} }
}); })
} }
UniversalDApp.prototype.runTx = function (args, cb) { UniversalDApp.prototype.runTx = function (args, cb) {
var self = this; var self = this
var to = args.to; var to = args.to
var data = args.data; var data = args.data
if (data.slice(0, 2) !== '0x') { if (data.slice(0, 2) !== '0x') {
data = '0x' + data; data = '0x' + data
} }
var gasLimit = 3000000; var gasLimit = 3000000
if (self.getGasLimit) { if (self.getGasLimit) {
try { try {
gasLimit = self.getGasLimit(); gasLimit = self.getGasLimit()
} catch (e) { } catch (e) {
return cb(e); return cb(e)
} }
} }
var value = 0; var value = 0
if (self.getValue) { if (self.getValue) {
try { try {
value = self.getValue(); value = self.getValue()
} catch (e) { } catch (e) {
return cb(e); return cb(e)
} }
} }
var tx; var tx
if (!self.executionContext.isVM()) { if (!self.executionContext.isVM()) {
tx = { tx = {
from: self.getAddress ? self.getAddress() : self.web3.eth.accounts[0], from: self.getAddress ? self.getAddress() : self.web3.eth.accounts[0],
to: to, to: to,
data: data, data: data,
value: value value: value
}; }
if (args.useCall) { if (args.useCall) {
tx.gas = gasLimit; tx.gas = gasLimit
self.web3.eth.call(tx, cb); self.web3.eth.call(tx, cb)
} else { } else {
self.web3.eth.estimateGas(tx, function (err, resp) { self.web3.eth.estimateGas(tx, function (err, resp) {
if (err) { if (err) {
return cb(err, resp); return cb(err, resp)
} }
if (resp > gasLimit) { if (resp > gasLimit) {
return cb('Gas required exceeds limit: ' + resp); return cb('Gas required exceeds limit: ' + resp)
} }
tx.gas = resp; tx.gas = resp
var sendTransaction = self.personalMode ? self.web3.personal.unlockAccountAndSendTransaction : self.web3.eth.sendTransaction; var sendTransaction = self.personalMode ? self.web3.personal.unlockAccountAndSendTransaction : self.web3.eth.sendTransaction
sendTransaction(tx, function (err, resp) { sendTransaction(tx, function (err, resp) {
if (err) { if (err) {
return cb(err, resp); return cb(err, resp)
} }
tryTillResponse(self.web3, resp, cb); tryTillResponse(self.web3, resp, cb)
}); })
}); })
} }
} else { } else {
try { try {
var address = self.getAddress ? self.getAddress() : Object.keys(self.accounts)[0]; var address = self.getAddress ? self.getAddress() : Object.keys(self.accounts)[0]
var account = self.accounts[address]; var account = self.accounts[address]
tx = new EthJSTX({ tx = new EthJSTX({
nonce: new BN(account.nonce++), nonce: new BN(account.nonce++),
gasPrice: new BN(1), gasPrice: new BN(1),
...@@ -746,8 +746,8 @@ UniversalDApp.prototype.runTx = function (args, cb) { ...@@ -746,8 +746,8 @@ UniversalDApp.prototype.runTx = function (args, cb) {
to: to, to: to,
value: new BN(value, 10), value: new BN(value, 10),
data: new Buffer(data.slice(2), 'hex') data: new Buffer(data.slice(2), 'hex')
}); })
tx.sign(account.privateKey); tx.sign(account.privateKey)
var block = new EthJSBlock({ var block = new EthJSBlock({
header: { header: {
// FIXME: support coinbase, difficulty and gasLimit // FIXME: support coinbase, difficulty and gasLimit
...@@ -756,18 +756,18 @@ UniversalDApp.prototype.runTx = function (args, cb) { ...@@ -756,18 +756,18 @@ UniversalDApp.prototype.runTx = function (args, cb) {
}, },
transactions: [], transactions: [],
uncleHeaders: [] uncleHeaders: []
}); })
if (!args.useCall) { if (!args.useCall) {
++self.blockNumber; ++self.blockNumber
} }
self.vm.runTx({block: block, tx: tx, skipBalance: true, skipNonce: true}, function (err, result) { self.vm.runTx({block: block, tx: tx, skipBalance: true, skipNonce: true}, function (err, result) {
result.transactionHash = self.txdebugger.web3().releaseCurrentHash(); // used to keep track of the transaction result.transactionHash = self.txdebugger.web3().releaseCurrentHash() // used to keep track of the transaction
cb(err, result); cb(err, result)
}); })
} catch (e) { } catch (e) {
cb(e, null); cb(e, null)
} }
} }
}; }
module.exports = UniversalDApp; module.exports = UniversalDApp
module.exports = { module.exports = {
checkCompiledContracts: function (browser, compiled, callback) { checkCompiledContracts: function (browser, compiled, callback) {
browser.execute(function () { browser.execute(function () {
var contracts = document.querySelectorAll('.udapp .contract'); var contracts = document.querySelectorAll('.udapp .contract')
var ret = []; var ret = []
for (var k in contracts) { for (var k in contracts) {
var el = contracts[k]; var el = contracts[k]
if (el.querySelector) { if (el.querySelector) {
ret.push({ ret.push({
name: el.querySelector('.title').innerText.replace(el.querySelector('.size').innerText, '').replace(/(\t)|(\r)|(\n)/g, '') // IE/firefox add \r\n name: el.querySelector('.title').innerText.replace(el.querySelector('.size').innerText, '').replace(/(\t)|(\r)|(\n)/g, '') // IE/firefox add \r\n
}); })
} }
} }
return ret; return ret
}, [''], function (result) { }, [''], function (result) {
browser.assert.equal(result.value.length, compiled.length); browser.assert.equal(result.value.length, compiled.length)
result.value.map(function (item, i) { result.value.map(function (item, i) {
browser.assert.equal(item.name, compiled[i]); browser.assert.equal(item.name, compiled[i])
}); })
callback(); callback()
}); })
} }
}; }
var Module = { // eslint-disable-line var Module = { // eslint-disable-line
cwrap: function () { return arguments[0] === 'version' ? version : compile; }, cwrap: function () { return arguments[0] === 'version' ? version : compile },
writeStringToMemory: function () {}, writeStringToMemory: function () {},
setValue: function () {}, setValue: function () {},
Pointer_stringify: function () {}, Pointer_stringify: function () {},
...@@ -10,32 +10,32 @@ var Module = { // eslint-disable-line ...@@ -10,32 +10,32 @@ var Module = { // eslint-disable-line
_compileJSONMulti: {}, _compileJSONMulti: {},
_compileJSONCallback: {}, _compileJSONCallback: {},
_compileJSON: {} _compileJSON: {}
}; }
function compile (source, optimization, missingInputs) { function compile (source, optimization, missingInputs) {
if (typeof source === 'string') { if (typeof source === 'string') {
source = JSON.parse(source); source = JSON.parse(source)
} }
var key = optimization.toString(); var key = optimization.toString()
for (var k in source.sources) { for (var k in source.sources) {
key += k + source.sources[k]; key += k + source.sources[k]
} }
key = key.replace(/(\t)|(\n)|( )/g, ''); key = key.replace(/(\t)|(\n)|( )/g, '')
var data = mockData[key]; // eslint-disable-line var data = mockData[key] // eslint-disable-line
if (data === undefined) { if (data === undefined) {
return JSON.stringify({ return JSON.stringify({
errors: ['mock compiler: source not found'] errors: ['mock compiler: source not found']
}); })
} else { } else {
data.missingInputs.map(function (item, i) { data.missingInputs.map(function (item, i) {
if (missingInputs) { if (missingInputs) {
missingInputs(item); missingInputs(item)
} }
}); })
} }
return JSON.stringify(data.result); return JSON.stringify(data.result)
} }
function version () { function version () {
return 'mock compiler'; return 'mock compiler'
} }
...@@ -6,72 +6,72 @@ module.exports = { ...@@ -6,72 +6,72 @@ module.exports = {
}, },
'ballot': { 'ballot': {
'sources': { 'sources': {
'Untitled': `pragma solidity ^0.4.0; 'Untitled': `pragma solidity ^0.4.0
contract Ballot { contract Ballot {
struct Voter { struct Voter {
uint weight; uint weight
bool voted; bool voted
uint8 vote; uint8 vote
address delegate; address delegate
} }
struct Proposal { struct Proposal {
uint voteCount; uint voteCount
} }
address chairperson; address chairperson
mapping(address => Voter) voters; mapping(address => Voter) voters
Proposal[] proposals; Proposal[] proposals
/// Create a new ballot with $(_numProposals) different proposals. /// Create a new ballot with $(_numProposals) different proposals.
function Ballot(uint8 _numProposals) { function Ballot(uint8 _numProposals) {
chairperson = msg.sender; chairperson = msg.sender
voters[chairperson].weight = 1; voters[chairperson].weight = 1
proposals.length = _numProposals; proposals.length = _numProposals
} }
/// Give $(voter) the right to vote on this ballot. /// Give $(voter) the right to vote on this ballot.
/// May only be called by $(chairperson). /// May only be called by $(chairperson).
function giveRightToVote(address voter) { function giveRightToVote(address voter) {
if (msg.sender != chairperson || voters[voter].voted) return; if (msg.sender != chairperson || voters[voter].voted) return
voters[voter].weight = 1; voters[voter].weight = 1
} }
/// Delegate your vote to the voter $(to). /// Delegate your vote to the voter $(to).
function delegate(address to) { function delegate(address to) {
Voter sender = voters[msg.sender]; // assigns reference Voter sender = voters[msg.sender] // assigns reference
if (sender.voted) return; if (sender.voted) return
while (voters[to].delegate != address(0) && voters[to].delegate != msg.sender) while (voters[to].delegate != address(0) && voters[to].delegate != msg.sender)
to = voters[to].delegate; to = voters[to].delegate
if (to == msg.sender) return; if (to == msg.sender) return
sender.voted = true; sender.voted = true
sender.delegate = to; sender.delegate = to
Voter delegate = voters[to]; Voter delegate = voters[to]
if (delegate.voted) if (delegate.voted)
proposals[delegate.vote].voteCount += sender.weight; proposals[delegate.vote].voteCount += sender.weight
else else
delegate.weight += sender.weight; delegate.weight += sender.weight
} }
/// Give a single vote to proposal $(proposal). /// Give a single vote to proposal $(proposal).
function vote(uint8 proposal) { function vote(uint8 proposal) {
Voter sender = voters[msg.sender]; Voter sender = voters[msg.sender]
if (sender.voted || proposal >= proposals.length) return; if (sender.voted || proposal >= proposals.length) return
sender.voted = true; sender.voted = true
sender.vote = proposal; sender.vote = proposal
proposals[proposal].voteCount += sender.weight; proposals[proposal].voteCount += sender.weight
} }
function winningProposal() constant returns (uint8 winningProposal) { function winningProposal() constant returns (uint8 winningProposal) {
uint256 winningVoteCount = 0; uint256 winningVoteCount = 0
for (uint8 proposal = 0; proposal < proposals.length; proposal++) for (uint8 proposal = 0; proposal < proposals.length; proposal++)
if (proposals[proposal].voteCount > winningVoteCount) { if (proposals[proposal].voteCount > winningVoteCount) {
winningVoteCount = proposals[proposal].voteCount; winningVoteCount = proposals[proposal].voteCount
winningProposal = proposal; winningProposal = proposal
} }
} }
} }
` `
} }
} }
}; }
'use strict'; 'use strict'
var testData = require('../mockcompiler/requests'); var testData = require('../mockcompiler/requests')
// var contractHelper = require('../helpers/contracts'); // var contractHelper = require('../helpers/contracts')
module.exports = { module.exports = {
'Ballot': function (browser) { 'Ballot': function (browser) {
runTests(browser, testData); runTests(browser, testData)
} }
}; }
function runTests (browser, testData) { function runTests (browser, testData) {
browser browser
.url('http://127.0.0.1:8080/#version=builtin') .url('http://127.0.0.1:8080/#version=builtin')
.waitForElementVisible('.newFile', 10000); .waitForElementVisible('.newFile', 10000)
browser.assert.notEqual(testData, null); browser.assert.notEqual(testData, null)
// TODO add Ballot tests. -> setValue('#input textarea', ... ) is not working properly with that contract. // TODO add Ballot tests. -> setValue('#input textarea', ... ) is not working properly with that contract.
/* testBallot(browser, testData.ballot.sources.Untitled, function () { /*
browser.end(); testBallot(browser, testData.ballot.sources.Untitled, function () {
});*/ browser.end()
});
*/
} }
/* /*
...@@ -25,7 +27,8 @@ function testBallot (browser, contract, callback) { ...@@ -25,7 +27,8 @@ function testBallot (browser, contract, callback) {
.click('.newFile') .click('.newFile')
.clearValue('#input textarea') .clearValue('#input textarea')
.setValue('#input textarea', contract, function () { .setValue('#input textarea', contract, function () {
browser.pause('10000'); browser.pause('10000')
contractHelper.checkCompiledContracts(browser, ['Ballot'], callback); contractHelper.checkCompiledContracts(browser, ['Ballot'], callback)
}); })
}*/ }
*/
'use strict'; 'use strict'
module.exports = { module.exports = {
'Debugger Render': function (browser) { 'Debugger Render': function (browser) {
...@@ -6,6 +6,6 @@ module.exports = { ...@@ -6,6 +6,6 @@ module.exports = {
.url('http://127.0.0.1:8080/#version=builtin') .url('http://127.0.0.1:8080/#version=builtin')
.waitForElementPresent('#debugger', 10000) .waitForElementPresent('#debugger', 10000)
.waitForElementPresent('#debugger #slider', 10000) .waitForElementPresent('#debugger #slider', 10000)
.end(); .end()
} }
}; }
'use strict'; 'use strict'
module.exports = { module.exports = {
'New file test': function (browser) { 'New file test': function (browser) {
...@@ -8,6 +8,6 @@ module.exports = { ...@@ -8,6 +8,6 @@ module.exports = {
.click('.newFile') .click('.newFile')
.pause('10000') .pause('10000')
.assert.containsText('.active', 'Untitled') .assert.containsText('.active', 'Untitled')
.end(); .end()
} }
}; }
'use strict'; 'use strict'
var testData = require('../mockcompiler/requests'); var testData = require('../mockcompiler/requests')
var contractHelper = require('../helpers/contracts'); var contractHelper = require('../helpers/contracts')
module.exports = { module.exports = {
'Simple Contract': function (browser) { 'Simple Contract': function (browser) {
runTests(browser, testData); runTests(browser, testData)
} }
}; }
function runTests (browser, testData) { function runTests (browser, testData) {
browser browser
.url('http://127.0.0.1:8080/#version=builtin') .url('http://127.0.0.1:8080/#version=builtin')
.waitForElementVisible('.newFile', 10000); .waitForElementVisible('.newFile', 10000)
browser.assert.notEqual(testData, null); browser.assert.notEqual(testData, null)
testSimpleContract(browser, testData.testSimpleContract.sources.Untitled, function () { testSimpleContract(browser, testData.testSimpleContract.sources.Untitled, function () {
browser.end(); browser.end()
}); })
} }
function testSimpleContract (browser, contract, callback) { function testSimpleContract (browser, contract, callback) {
...@@ -23,6 +23,6 @@ function testSimpleContract (browser, contract, callback) { ...@@ -23,6 +23,6 @@ function testSimpleContract (browser, contract, callback) {
.click('.newFile') .click('.newFile')
.clearValue('#input textarea') .clearValue('#input textarea')
.setValue('#input textarea', contract) .setValue('#input textarea', contract)
.pause('5000'); .pause('5000')
contractHelper.checkCompiledContracts(browser, ['test1', 'test2'], callback); contractHelper.checkCompiledContracts(browser, ['test1', 'test2'], callback)
} }
'use strict'; 'use strict'
module.exports = { module.exports = {
'Smoke test': function (browser) { 'Smoke test': function (browser) {
...@@ -7,6 +7,6 @@ module.exports = { ...@@ -7,6 +7,6 @@ module.exports = {
.waitForElementVisible('#righthand-panel', 10000) .waitForElementVisible('#righthand-panel', 10000)
.pause('10000') .pause('10000')
.assert.containsText('#righthand-panel', 'Solidity version') .assert.containsText('#righthand-panel', 'Solidity version')
.end(); .end()
} }
}; }
'use strict'; 'use strict'
var test = require('tape'); var test = require('tape')
var Compiler = require('../src/app/compiler'); var Compiler = require('../src/app/compiler')
var EventManager = require('../src/lib/eventManager'); var EventManager = require('../src/lib/eventManager')
test('compiler.compile smoke', function (t) { test('compiler.compile smoke', function (t) {
t.plan(1); t.plan(1)
var noop = function () {}; var noop = function () {}
var getCacheFile = function () { return 'fakeCacheFile'; }; var getCacheFile = function () { return 'fakeCacheFile' }
var fakeEditor = {onChangeSetup: noop, clearAnnotations: noop, getValue: noop, setCacheFileContent: noop, getCacheFile: getCacheFile}; var fakeEditor = {onChangeSetup: noop, clearAnnotations: noop, getValue: noop, setCacheFileContent: noop, getCacheFile: getCacheFile}
var fakeQueryParams = {get: function () { return {}; }}; var fakeQueryParams = {get: function () { return {} }}
var compiler = new Compiler(fakeEditor, fakeQueryParams, null, null, new EventManager()); var compiler = new Compiler(fakeEditor, fakeQueryParams, null, null, new EventManager())
compiler.setCompileJSON(noop); compiler.setCompileJSON(noop)
compiler.compile(); compiler.compile()
t.ok(compiler); t.ok(compiler)
}); })
'use strict'; 'use strict'
var test = require('tape'); var test = require('tape')
var GistHandler = require('../src/app/gist-handler'); var GistHandler = require('../src/app/gist-handler')
test('gistHandler.handleLoad with no gist param', function (t) { test('gistHandler.handleLoad with no gist param', function (t) {
t.plan(1); t.plan(1)
var gistHandler = new GistHandler({}); var gistHandler = new GistHandler({})
var params = {}; var params = {}
var result = gistHandler.handleLoad(params, null); var result = gistHandler.handleLoad(params, null)
t.equal(result, false); t.equal(result, false)
}); })
test('gistHandler.handleLoad with blank gist param, and invalid user input', function (t) { test('gistHandler.handleLoad with blank gist param, and invalid user input', function (t) {
t.plan(3); t.plan(3)
var fakeWindow = {prompt: function (message) { var fakeWindow = {prompt: function (message) {
t.ok(message); t.ok(message)
t.ok(message.match(/gist/i)); t.ok(message.match(/gist/i))
return 'invalid'; return 'invalid'
}}; }}
var gistHandler = new GistHandler(fakeWindow); var gistHandler = new GistHandler(fakeWindow)
var params = {'gist': ''}; var params = {'gist': ''}
var result = gistHandler.handleLoad(params, null); var result = gistHandler.handleLoad(params, null)
t.equal(result, false); t.equal(result, false)
}); })
test('gistHandler.handleLoad with blank gist param, and valid user input', function (t) { test('gistHandler.handleLoad with blank gist param, and valid user input', function (t) {
t.plan(4); t.plan(4)
var fakeWindow = {prompt: function (message) { var fakeWindow = {prompt: function (message) {
t.ok(message); t.ok(message)
t.ok(message.match(/gist/i)); t.ok(message.match(/gist/i))
return 'Beef1234'; return 'Beef1234'
}}; }}
var cb = function (gistId) { var cb = function (gistId) {
t.equal(gistId, 'Beef1234'); t.equal(gistId, 'Beef1234')
}; }
var gistHandler = new GistHandler(fakeWindow); var gistHandler = new GistHandler(fakeWindow)
var params = {'gist': ''}; var params = {'gist': ''}
var result = gistHandler.handleLoad(params, cb); var result = gistHandler.handleLoad(params, cb)
t.equal(result, true); t.equal(result, true)
}); })
test('gistHandler.handleLoad with gist param', function (t) { test('gistHandler.handleLoad with gist param', function (t) {
t.plan(2); t.plan(2)
var gistHandler = new GistHandler({}); var gistHandler = new GistHandler({})
var params = {'gist': 'abc'}; var params = {'gist': 'abc'}
var cb = function (gistId) { var cb = function (gistId) {
t.equal(gistId, 'abc'); t.equal(gistId, 'abc')
}; }
var result = gistHandler.handleLoad(params, cb); var result = gistHandler.handleLoad(params, cb)
t.equal(result, true);
});
t.equal(result, true)
})
'use strict'; 'use strict'
require('./compiler-test'); require('./compiler-test')
require('./gist-handler-test'); require('./gist-handler-test')
require('./query-params-test'); require('./query-params-test')
'use strict'; 'use strict'
var test = require('tape'); var test = require('tape')
var QueryParams = require('../src/app/query-params'); var QueryParams = require('../src/app/query-params')
test('queryParams.get', function (t) { test('queryParams.get', function (t) {
t.plan(2); t.plan(2)
var fakeWindow = {location: {hash: '#wat=sup&foo=bar', search: ''}}; var fakeWindow = {location: {hash: '#wat=sup&foo=bar', search: ''}}
var params = new QueryParams(fakeWindow).get(); var params = new QueryParams(fakeWindow).get()
t.equal(params.wat, 'sup'); t.equal(params.wat, 'sup')
t.equal(params.foo, 'bar'); t.equal(params.foo, 'bar')
}); })
test('queryParams.update', function (t) { test('queryParams.update', function (t) {
t.plan(1); t.plan(1)
var fakeWindow = {location: {hash: '#wat=sup', search: ''}}; var fakeWindow = {location: {hash: '#wat=sup', search: ''}}
var qp = new QueryParams(fakeWindow); var qp = new QueryParams(fakeWindow)
qp.update({foo: 'bar'}); qp.update({foo: 'bar'})
t.equal(fakeWindow.location.hash, '#wat=sup&foo=bar'); t.equal(fakeWindow.location.hash, '#wat=sup&foo=bar')
}); })
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment