Commit 9e7fe56e authored by chriseth's avatar chriseth

Merge branch 'd11e9-gh-pages' into preservePreviousContent

Conflicts: index.html
parents ec718645 ad567503
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
var multi = function(func) { return func.toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1]; } var multi = function(func) { return func.toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1]; }
var BALLOT_EXAMPLE = multi(function(){/*contract Ballot { var BALLOT_EXAMPLE = multi(function(){/*contract Ballot {
struct Voter { struct Voter {
uint weight; uint weight;
bool voted; bool voted;
......
...@@ -47,463 +47,590 @@ THE SOFTWARE. ...@@ -47,463 +47,590 @@ THE SOFTWARE.
</head> </head>
<body> <body>
<div id="editor"> <div id="editor">
<div id="input"></div> <div id="files">
</div> <span class="newFile" title="New File">+</span>
</div>
<div id="righthand-panel"> <div id="input"></div>
<div id="dragbar"></div> </div>
<div id="header">
<img id="solIcon" src="solidity.svg"> <div id="righthand-panel">
<h1>Solidity realtime<br/>compiler and runtime</h1> <div id="dragbar"></div>
<div class="info"> <div id="header">
<p>Version: <span id="version">(loading)</span><br/> <img id="solIcon" src="solidity.svg">
Change to: <select id="versionSelector"></select><br/> <h1>Solidity realtime<br/>compiler and runtime</h1>
Execution environment does not connect to any node, everyhing is local and in memory only.<br/> <div class="info">
<code>tx.origin = <span id="txorigin"/></code></p> <p>Version: <span id="version">(loading)</span><br/>
</div> Change to: <select id="versionSelector"></select><br/>
<div id="optimizeBox"> Execution environment does not connect to any node, everyhing is local and in memory only.<br/>
<input id="editorWrap" type="checkbox"><label for="editorWrap">Text Wrap</label> <code>tx.origin = <span id="txorigin"/></code></p>
<input id="optimize" type="checkbox"><label for="optimize">Enable Optimization</label> </div>
</div> <div id="optimizeBox">
</div> <input id="editorWrap" type="checkbox"><label for="editorWrap">Text Wrap</label>
<div id="output"></div> <input id="optimize" type="checkbox"><label for="optimize">Enable Optimization</label>
</div> </div>
</div>
<div id="output"></div>
</div>
<script>
// ----------------- editor ---------------------- <script>
var SOL_CACHE_KEY = "sol-cache";
// ----------------- editor ----------------------
var editor = ace.edit("input");
var session = editor.getSession(); var SOL_CACHE_FILE = "Untitled"
var Range = ace.require('ace/range').Range; var SOL_CACHE_FILES_KEY = "sol-cache-files";
var errMarkerId = null;
var editor = ace.edit("input");
var solCache = window.localStorage.getItem( SOL_CACHE_KEY ); var session = editor.getSession();
editor.setValue( solCache || BALLOT_EXAMPLE, 1 ); var Range = ace.require('ace/range').Range;
var errMarkerId = null;
session.setMode("ace/mode/javascript");
session.setTabSize(4); var solFiles = JSON.parse( window.localStorage.getItem( SOL_CACHE_FILES_KEY ) ) || [SOL_CACHE_FILE];
session.setUseSoftTabs(true); if (solFiles.length === 0) solFiles = [SOL_CACHE_FILE];
window.localStorage.setItem( SOL_CACHE_FILES_KEY, JSON.stringify( solFiles ) );
// ----------------- version selector-------------
var solCache = window.localStorage.getItem( SOL_CACHE_FILE ) || BALLOT_EXAMPLE;
// var soljsonSources is provided by bin/list.js window.localStorage.setItem( SOL_CACHE_FILE, solCache )
$('option', '#versionSelector').remove(); editor.setValue( solCache, 1 );
$.each(soljsonSources, function(i, file) {
if (file) {
var version = file.replace(/soljson-(.*).js/, "$1"); session.setMode("ace/mode/javascript");
$('#versionSelector').append(new Option(version, file)); session.setTabSize(4);
} session.setUseSoftTabs(true);
});
$('#versionSelector').change(function() { // ----------------- file selector-------------
Module = null;
compileJSON = null; var count = 0;
var script = document.createElement('script'); var $filesEl = $('#files');
script.type = 'text/javascript';
script.src = 'bin/' + $('#versionSelector').val(); $filesEl.on( 'click','.newFile', function(){
$('head').append(script); count++;
onCompilerLoaded(); var name = 'Unititled'+count;
}); solFiles.push( name )
SOL_CACHE_FILE = name;
// ----------------- resizeable ui --------------- window.localStorage.setItem( SOL_CACHE_FILES_KEY, JSON.stringify( solFiles ) );
window.localStorage.setItem( SOL_CACHE_FILE, '' );
var EDITOR_SIZE_CACHE_KEY = "editor-size-cache"; updateFiles();
var dragging = false; })
$('#dragbar').mousedown(function(e){
e.preventDefault(); $filesEl.on( 'click','.file:not(.active)', showFileHandler )
dragging = true;
var main = $('#righthand-panel'); $filesEl.on( 'click','.file.active', function(ev){
var ghostbar = $('<div id="ghostbar">', { var $fileTabEl = $(this)
css: { var originalName = $fileTabEl.find('.name').text()
top: main.offset().top, ev.preventDefault()
left: main.offset().left if ($(this).find('input').length > 0 ) return false;
} var $fileNameInputEl = $('<input value="'+originalName+'"/>');
}).prependTo('body'); $fileTabEl.html( $fileNameInputEl )
$fileNameInputEl.focus()
$(document).mousemove(function(e){ $fileNameInputEl.select()
ghostbar.css("left",e.pageX+2); $fileNameInputEl.on( 'blur', handleRename )
}); $fileNameInputEl.keyup( handleRename );
});
function handleRename (ev) {
var $body = $('body'); ev.preventDefault()
if (ev.which && ev.which !== 13) return false;
function setEditorSize (delta) { var newName = ev.target.value;
$('#righthand-panel').css("width", delta); var $new = null
$('#editor').css("right", delta); if ( newName !== originalName && confirm( "Are you sure you want to rename: " + originalName + " to " + newName + '?' )) {
onResize();
} solFiles.splice( solFiles.indexOf(originalName), 1, newName );
window.localStorage.setItem( newName, window.localStorage.getItem(originalName) );
$(document).mouseup(function(e){ window.localStorage.setItem( SOL_CACHE_FILES_KEY, JSON.stringify( solFiles ) );
if (dragging) { window.localStorage.removeItem( originalName )
var delta = $body.width() - e.pageX+2; SOL_CACHE_FILE = newName;
$('#ghostbar').remove(); }
$(document).unbind('mousemove');
dragging = false; updateFiles()
setEditorSize( delta ) return false;
window.localStorage.setItem( EDITOR_SIZE_CACHE_KEY, delta ); }
}
}); return false;
})
// set cached defaults
var cachedSize = window.localStorage.getItem( EDITOR_SIZE_CACHE_KEY ); $filesEl.on( 'click','.file .remove', function(ev){
if (cachedSize) setEditorSize( cachedSize ); ev.preventDefault();
var name = $(this).parent().find('.name').text()
// ----------------- editor resize --------------- if (confirm( "Are you sure you want to remove: " + name + " from local storage?" )) {
var index = solFiles.indexOf(name);
function onResize() { solFiles.splice(index, 1 );
editor.resize(); window.localStorage.setItem( SOL_CACHE_FILES_KEY, JSON.stringify( solFiles ) );
session.setUseWrapMode(document.querySelector('#editorWrap').checked); SOL_CACHE_FILE = solFiles[ Math.max(0, index - 1)]
if(session.getUseWrapMode()) { window.localStorage.removeItem( name )
var characterWidth = editor.renderer.characterWidth; updateFiles()
var contentWidth = editor.container.ownerDocument.getElementsByClassName("ace_scroller")[0].clientWidth; }
return false;
if(contentWidth > 0) { });
session.setWrapLimit(parseInt(contentWidth / characterWidth, 10));
} function showFileHandler (ev) {
} ev.preventDefault()
} SOL_CACHE_FILE = $(this).find('.name').text();
window.onresize = onResize; updateFiles()
onResize(); return false;
}
document.querySelector('#editor').addEventListener('change', onResize );
function fileTabFromName(name) {
return $('#files .file').filter(function(){ return $(this).find('.name').text() == name; })
// ----------------- compiler ---------------------- }
var compileJSON;
function updateFiles () {
var previousInput = ''; $filesEl.find('.file').remove()
var sourceAnnotations = []; for (var f in solFiles) {
var compile = function() { if (solFiles[f]) $filesEl.append( fileTabTemplate(solFiles[f]) );
editor.getSession().clearAnnotations(); }
sourceAnnotations = []; var active = fileTabFromName(SOL_CACHE_FILE);
editor.getSession().removeMarker(errMarkerId); active.addClass('active')
$('#output').empty(); editor.setValue( window.localStorage.getItem( SOL_CACHE_FILE ) || '', 1 );
var input = editor.getValue(); $('#input').toggle( typeof SOL_CACHE_FILE === 'string' )
var optimize = document.querySelector('#optimize').checked; }
try {
var data = $.parseJSON(compileJSON(input, optimize ? 1 : 0)); function fileTabTemplate(name){
} catch (exception) { return $('<span class="file"><span class="name">'+name+'</span><span class="remove">x</span></span>');
renderError("Uncaught JavaScript Exception:\n" + exception); }
return;
} SOL_CACHE_FILE = solFiles[0]
if (data['error'] !== undefined) updateFiles();
renderError(data['error']);
if (data['errors'] != undefined) // ----------------- version selector-------------
$.each(data['errors'], function(i, err) {
renderError(err); // var soljsonSources is provided by bin/list.js
}); $('option', '#versionSelector').remove();
else $.each(soljsonSources, function(i, file) {
renderContracts(data, input); if (file) {
var version = file.replace(/soljson-(.*).js/, "$1");
} $('#versionSelector').append(new Option(version, file));
var compileTimeout = null; }
var onChange = function() { });
var input = editor.getValue(); $('#versionSelector').change(function() {
if (input === "") { Module = null;
window.localStorage.setItem( SOL_CACHE_KEY, '' ) compileJSON = null;
return; var script = document.createElement('script');
} script.type = 'text/javascript';
if (input === previousInput) script.src = 'bin/' + $('#versionSelector').val();
return; $('head').append(script);
previousInput = input; onCompilerLoaded();
if (compileTimeout) window.clearTimeout(compileTimeout); });
compileTimeout = window.setTimeout(compile, 300);
}; // ----------------- resizeable ui ---------------
var onCompilerLoaded = function() { var EDITOR_SIZE_CACHE_KEY = "editor-size-cache";
compileJSON = Module.cwrap("compileJSON", "string", ["string", "number"]); var dragging = false;
$('#version').text(Module.cwrap("version", "string", [])()); $('#dragbar').mousedown(function(e){
previousInput = ''; e.preventDefault();
onChange(); dragging = true;
}; var main = $('#righthand-panel');
if (Module) var ghostbar = $('<div id="ghostbar">', {
onCompilerLoaded(); css: {
top: main.offset().top,
editor.getSession().on('change', onChange); left: main.offset().left
}
document.querySelector('#optimize').addEventListener('change', compile); }).prependTo('body');
// ----------------- compiler output renderer ---------------------- $(document).mousemove(function(e){
var detailsOpen = {}; ghostbar.css("left",e.pageX+2);
});
var renderError = function(message) { });
$('#output')
.append($('<pre class="error"></pre>').text(message)); var $body = $('body');
var err = message.match(/^:([0-9]*):([0-9]*)/)
if (err && err.length) { function setEditorSize (delta) {
var errLine = parseInt( err[1], 10 ) - 1; $('#righthand-panel').css("width", delta);
var errCol = err[2] ? parseInt( err[2], 10 ) : 0; $('#editor').css("right", delta);
sourceAnnotations[sourceAnnotations.length] ={ onResize();
row: errLine, }
column: errCol,
text: message, $(document).mouseup(function(e){
type: "error" if (dragging) {
}; var delta = $body.width() - e.pageX+2;
editor.getSession().setAnnotations(sourceAnnotations); $('#ghostbar').remove();
} $(document).unbind('mousemove');
}; dragging = false;
setEditorSize( delta )
var gethDeploy = function(contractName, interface, bytecode){ window.localStorage.setItem( EDITOR_SIZE_CACHE_KEY, delta );
var code = ""; }
var funABI = getConstructorInterface($.parseJSON(interface)); });
$.each(funABI.inputs, function(i, inp) { // set cached defaults
code += "var "+inp.name+" = /* var of type " + inp.type + " here */ ;\n"; var cachedSize = window.localStorage.getItem( EDITOR_SIZE_CACHE_KEY );
}); if (cachedSize) setEditorSize( cachedSize );
code += "\nvar "+contractName+"Contract = web3.eth.contract("+interface.replace("\n","")+");"
+"\nvar "+contractName+" = "+contractName+"Contract.new("; // ----------------- editor resize ---------------
$.each(funABI.inputs, function(i, inp) { function onResize() {
code += "\n "+inp.name+","; editor.resize();
}); session.setUseWrapMode(document.querySelector('#editorWrap').checked);
if(session.getUseWrapMode()) {
code += "\n {"+ var characterWidth = editor.renderer.characterWidth;
"\n from: web3.eth.accounts[0], "+ var contentWidth = editor.container.ownerDocument.getElementsByClassName("ace_scroller")[0].clientWidth;
"\n data: '"+bytecode+"', "+
"\n gas: 3000000"+ if(contentWidth > 0) {
"\n }, function(e, contract){"+ session.setWrapLimit(parseInt(contentWidth / characterWidth, 10));
"\n if (typeof contract.address != 'undefined') {"+ }
"\n console.log(e, contract);"+ }
"\n console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);" + }
"\n }" + window.onresize = onResize;
"\n })"; onResize();
document.querySelector('#editor').addEventListener('change', onResize );
return code;
};
// ----------------- compiler ----------------------
var combined = function(contractName, interface, bytecode){ var compileJSON;
return JSON.stringify( [{name: contractName, interface: interface, bytecode: bytecode}]);
var previousInput = '';
}; var sourceAnnotations = [];
var compile = function() {
var renderContracts = function(data, source) {
window.localStorage.setItem( SOL_CACHE_KEY, source ); editor.getSession().clearAnnotations();
sourceAnnotations = [];
$('#output').empty(); editor.getSession().removeMarker(errMarkerId);
for (var contractName in data.contracts) { $('#output').empty();
var contract = data.contracts[contractName]; var input = editor.getValue();
var title = $('<h3 class="title"/>').text(contractName); window.localStorage.setItem( SOL_CACHE_FILE, input );
var contractOutput = $('<div class="contractOutput"/>')
.append(title); var inputIncludingImports = includeLocalImports( input );
var body = $('<div class="body" />') var optimize = document.querySelector('#optimize').checked;
contractOutput.append( body );
if (contract.bytecode.length > 0) try {
title.append($('<div class="size"/>').text((contract.bytecode.length / 2) + ' bytes')) var data = $.parseJSON(compileJSON(inputIncludingImports, optimize ? 1 : 0));
body.append(getExecuteInterface(contract, contractName)) } catch (exception) {
.append(tableRow('Bytecode', contract.bytecode)); renderError("Uncaught JavaScript Exception:\n" + exception);
body.append(tableRow('Interface', contract['interface'])) return;
.append(textRow('Web3 deploy', gethDeploy(contractName.toLowerCase(),contract['interface'],contract.bytecode), 'deploy')) }
.append(textRow('uDApp', combined(contractName,contract['interface'],contract.bytecode), 'deploy')) if (data['error'] !== undefined)
.append(getDetails(contract, source, contractName)); renderError(data['error']);
if (data['errors'] != undefined)
$('#output').append(contractOutput); $.each(data['errors'], function(i, err) {
title.click(function(ev){ $(this).parent().toggleClass('hide') }); renderError(err);
} });
else
$('.col2 input,textarea').click(function() { this.select(); } ); renderContracts(data, input);
};
var tableRowItems = function(first, second, cls) { }
return $('<div class="row"/>')
.addClass(cls) var compileTimeout = null;
.append($('<div class="col1">').append(first)) var onChange = function() {
.append($('<div class="col2">').append(second)); var input = editor.getValue();
}; if (input === "") {
var tableRow = function(description, data) { window.localStorage.setItem( SOL_CACHE_FILE, '' )
return tableRowItems( return;
$('<span/>').text(description), }
$('<input readonly="readonly"/>').val(data)); if (input === previousInput)
}; return;
var textRow = function(description, data, cls) { previousInput = input;
return tableRowItems( if (compileTimeout) window.clearTimeout(compileTimeout);
$('<strong/>').text(description), compileTimeout = window.setTimeout(compile, 300);
$('<textarea readonly="readonly" class="gethDeployText"/>').val(data), };
cls);
}; var onCompilerLoaded = function() {
var getDetails = function(contract, source, contractName) { compileJSON = Module.cwrap("compileJSON", "string", ["string", "number"]);
var button = $('<button>Details</button>'); $('#version').text(Module.cwrap("version", "string", [])());
var details = $('<div style="display: none;"/>') previousInput = '';
.append(tableRow('Solidity Interface', contract.solidity_interface)) onChange();
.append(tableRow('Opcodes', contract.opcodes)); };
var funHashes = '';
for (var fun in contract.functionHashes) function includeLocalImports( input ) {
funHashes += contract.functionHashes[fun] + ' ' + fun + '\n'; var importRegex = /import\s[\'\"]([^\'\"]+)[\'\"];/g
details.append($('<span class="col1">Functions</span>')); var imports = [];
details.append($('<pre/>').text(funHashes)); var matches = [];
details.append($('<span class="col1">Gas Estimates</span>')); var match;
details.append($('<pre/>').text(formatGasEstimates(contract.gasEstimates))); while ((match = importRegex.exec(input)) !== null) {
if (contract.runtimeBytecode && contract.runtimeBytecode.length > 0) if (match[1] && solFiles.indexOf(match[1]) !== -1) {
details.append(tableRow('Runtime Bytecode', contract.runtimeBytecode)); imports.push( match[1] )
if (contract.assembly !== null) matches.push( match[0] )
{ }
details.append($('<span class="col1">Assembly</span>')); }
var assembly = $('<pre/>').text(formatAssemblyText(contract.assembly, '', source)); for (var i in imports) {
details.append(assembly); imported = includeLocalImports( window.localStorage.getItem( imports[i] ) )
} input = input.replace( matches[i], imported );
button.click(function() { detailsOpen[contractName] = !detailsOpen[contractName]; details.toggle(); }); }
if (detailsOpen[contractName]) return input;
details.show(); }
return $('<div/>').append(button).append(details);
}; if (Module)
var formatGasEstimates = function(data) { onCompilerLoaded();
var gasToText = function(g) { return g === null ? 'unknown' : g; }
var text = ''; editor.getSession().on('change', onChange);
if ('creation' in data)
text += 'Creation: ' + gasToText(data.creation[0]) + ' + ' + gasToText(data.creation[1]) + '\n'; document.querySelector('#optimize').addEventListener('change', compile);
text += 'External:\n';
for (var fun in data.external) // ----------------- compiler output renderer ----------------------
text += ' ' + fun + ': ' + gasToText(data.external[fun]) + '\n'; var detailsOpen = {};
text += 'Internal:\n';
for (var fun in data.internal) var renderError = function(message) {
text += ' ' + fun + ': ' + gasToText(data.internal[fun]) + '\n'; $('#output')
return text; .append($('<pre class="error"></pre>').text(message));
}; var err = message.match(/^:([0-9]*):([0-9]*)/)
var formatAssemblyText = function(asm, prefix, source) { if (err && err.length) {
if (typeof(asm) == typeof('') || asm === null || asm === undefined) var errLine = parseInt( err[1], 10 ) - 1;
return prefix + asm + '\n'; var errCol = err[2] ? parseInt( err[2], 10 ) : 0;
var text = prefix + '.code\n'; sourceAnnotations[sourceAnnotations.length] ={
$.each(asm['.code'], function(i, item) { row: errLine,
var v = item.value === undefined ? '' : item.value; column: errCol,
var src = ''; text: message,
if (item.begin !== undefined && item.end != undefined) type: "error"
src = source.slice(item.begin, item.end).replace('\n', '\\n', 'g'); };
if (src.length > 30) editor.getSession().setAnnotations(sourceAnnotations);
src = src.slice(0, 30) + '...'; }
if (item.name != 'tag') };
text += ' ';
text += prefix + item.name + ' ' + v + '\t\t\t' + src + '\n'; var gethDeploy = function(contractName, interface, bytecode){
}); var code = "";
text += prefix + '.data\n'; var funABI = getConstructorInterface($.parseJSON(interface));
if (asm['.data'])
$.each(asm['.data'], function(i, item) { $.each(funABI.inputs, function(i, inp) {
text += ' ' + prefix + '' + i + ':\n'; code += "var "+inp.name+" = /* var of type " + inp.type + " here */ ;\n";
text += formatAssemblyText(item, prefix + ' ', source); });
});
code += "\nvar "+contractName+"Contract = web3.eth.contract("+interface.replace("\n","")+");"
return text; +"\nvar "+contractName+" = "+contractName+"Contract.new(";
};
$('.asmOutput button').click(function() {$(this).parent().find('pre').toggle(); } ) $.each(funABI.inputs, function(i, inp) {
code += "\n "+inp.name+",";
// ----------------- VM ---------------------- });
var stateTrie = new EthVm.Trie(); code += "\n {"+
var vm = new EthVm.VM(stateTrie); "\n from: web3.eth.accounts[0], "+
//@todo this does not calculate the gas costs correctly but gets the job done. "\n data: '"+bytecode+"', "+
var identityCode = 'return { gasUsed: 1, return: opts.data, exception: 1 };'; "\n gas: 3000000"+
var identityAddr = ethUtil.pad(new Buffer('04', 'hex'), 20) "\n }, function(e, contract){"+
vm.loadPrecompiled(identityAddr, identityCode); "\n if (typeof contract.address != 'undefined') {"+
var secretKey = '3cd7232cd6f3fc66a57a6bedc1a8ed6c228fff0a327e169c2bcc5e869ed49511' "\n console.log(e, contract);"+
var publicKey = '0406cc661590d48ee972944b35ad13ff03c7876eae3fd191e8a2f77311b0a3c6613407b5005e63d7d8d76b89d5f900cde691497688bb281e07a5052ff61edebdc0' "\n console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);" +
var address = ethUtil.pubToAddress(new Buffer(publicKey, 'hex')); "\n }" +
$('#txorigin').text('0x' + address.toString('hex')); "\n })";
var account = new EthVm.Account();
account.balance = 'f00000000000000001';
var nonce = 0; return code;
stateTrie.put(address, account.serialize()); };
var runTx = function(data, to, cb) {
var tx = new EthVm.Transaction({ var combined = function(contractName, interface, bytecode){
nonce: new Buffer([nonce++]), //@todo count beyond 255 return JSON.stringify( [{name: contractName, interface: interface, bytecode: bytecode}]);
gasPrice: '01',
gasLimit: '3000000000', // plenty };
to: to,
data: data var renderContracts = function(data, source) {
}); $('#output').empty();
tx.sign(new Buffer(secretKey, 'hex')); for (var contractName in data.contracts) {
vm.runTx({tx: tx}, cb); var contract = data.contracts[contractName];
}; var title = $('<h3 class="title"/>').text(contractName);
var contractOutput = $('<div class="contractOutput"/>')
var getConstructorInterface = function(abi) { .append(title);
var funABI = {'name':'','inputs':[],'type':'constructor','outputs':[]}; var body = $('<div class="body" />')
for (var i = 0; i < abi.length; i++) contractOutput.append( body );
if (abi[i].type == 'constructor') { if (contract.bytecode.length > 0)
funABI.inputs = abi[i].inputs || []; title.append($('<div class="size"/>').text((contract.bytecode.length / 2) + ' bytes'))
break; body.append(getExecuteInterface(contract, contractName))
} .append(tableRow('Bytecode', contract.bytecode));
return funABI; body.append(tableRow('Interface', contract['interface']))
}; .append(textRow('Web3 deploy', gethDeploy(contractName.toLowerCase(),contract['interface'],contract.bytecode), 'deploy'))
.append(textRow('uDApp', combined(contractName,contract['interface'],contract.bytecode), 'deploy'))
var getCallButton = function(args) { .append(getDetails(contract, source, contractName));
// args.abi, args.bytecode [constr only], args.address [fun only]
// args.appendFunctions [constr only] $('#output').append(contractOutput);
var isConstructor = args.bytecode !== undefined; title.click(function(ev){ $(this).parent().toggleClass('hide') });
var fun = new web3.eth.function(args.abi); }
var inputs = '';
$.each(args.abi.inputs, function(i, inp) { $('.col2 input,textarea').click(function() { this.select(); } );
if (inputs != '') inputs += ', '; };
inputs += inp.type + ' ' + inp.name; var tableRowItems = function(first, second, cls) {
}); return $('<div class="row"/>')
var inputField = $('<input/>').attr('placeholder', inputs); .addClass(cls)
var outputSpan = $('<div class="output"/>'); .append($('<div class="col1">').append(first))
var button = $('<button/>') .append($('<div class="col2">').append(second));
.text(args.bytecode ? 'Create' : fun.displayName()) };
.click(function() { var tableRow = function(description, data) {
var funArgs = $.parseJSON('[' + inputField.val() + ']'); return tableRowItems(
var data = fun.toPayload(funArgs).data; $('<span/>').text(description),
if (data.slice(0, 2) == '0x') data = data.slice(2); $('<input readonly="readonly"/>').val(data));
if (isConstructor) };
data = args.bytecode + data.slice(8); var textRow = function(description, data, cls) {
outputSpan.text('...'); return tableRowItems(
runTx(data, args.address, function(err, result) { $('<strong/>').text(description),
if (err) $('<textarea readonly="readonly" class="gethDeployText"/>').val(data),
outputSpan.text(err); cls);
else if (isConstructor) { };
outputSpan.text(' Creation used ' + result.vm.gasUsed.toString(10) + ' gas.'); var getDetails = function(contract, source, contractName) {
args.appendFunctions(result.createdAddress); var button = $('<button>Details</button>');
} else { var details = $('<div style="display: none;"/>')
var outputObj = fun.unpackOutput('0x' + result.vm.return.toString('hex')); .append(tableRow('Solidity Interface', contract.solidity_interface))
outputSpan.text(' Returned: ' + JSON.stringify(outputObj)); .append(tableRow('Opcodes', contract.opcodes));
} var funHashes = '';
}); for (var fun in contract.functionHashes)
}); funHashes += contract.functionHashes[fun] + ' ' + fun + '\n';
if (!isConstructor) details.append($('<span class="col1">Functions</span>'));
button.addClass('runButton'); details.append($('<pre/>').text(funHashes));
var c = $('<div class="contractProperty"/>') details.append($('<span class="col1">Gas Estimates</span>'));
.append(button); details.append($('<pre/>').text(formatGasEstimates(contract.gasEstimates)));
if (args.abi.inputs.length > 0) if (contract.runtimeBytecode && contract.runtimeBytecode.length > 0)
c.append(inputField); details.append(tableRow('Runtime Bytecode', contract.runtimeBytecode));
return c.append(outputSpan); if (contract.assembly !== null)
}; {
details.append($('<span class="col1">Assembly</span>'));
var getExecuteInterface = function(contract, name) { var assembly = $('<pre/>').text(formatAssemblyText(contract.assembly, '', source));
var abi = $.parseJSON(contract.interface); details.append(assembly);
var execInter = $('<div/>'); }
var funABI = getConstructorInterface(abi); button.click(function() { detailsOpen[contractName] = !detailsOpen[contractName]; details.toggle(); });
if (detailsOpen[contractName])
var appendFunctions = function(address) { details.show();
var instance = $('<div class="contractInstance"/>'); return $('<div/>').append(button).append(details);
var title = $('<span class="title"/>').text('Contract at ' + address.toString('hex') ); };
instance.append(title); var formatGasEstimates = function(data) {
$.each(abi, function(i, funABI) { var gasToText = function(g) { return g === null ? 'unknown' : g; }
if (funABI.type != 'function') return; var text = '';
instance.append(getCallButton({ if ('creation' in data)
abi: funABI, text += 'Creation: ' + gasToText(data.creation[0]) + ' + ' + gasToText(data.creation[1]) + '\n';
address: address text += 'External:\n';
})); for (var fun in data.external)
}); text += ' ' + fun + ': ' + gasToText(data.external[fun]) + '\n';
execInter.append(instance); text += 'Internal:\n';
title.click(function(ev){ $(this).parent().toggleClass('hide') }); for (var fun in data.internal)
}; text += ' ' + fun + ': ' + gasToText(data.internal[fun]) + '\n';
return text;
if (contract.bytecode.length > 0) };
execInter var formatAssemblyText = function(asm, prefix, source) {
.append(getCallButton({ if (typeof(asm) == typeof('') || asm === null || asm === undefined)
abi: funABI, return prefix + asm + '\n';
bytecode: contract.bytecode, var text = prefix + '.code\n';
appendFunctions: appendFunctions $.each(asm['.code'], function(i, item) {
})); var v = item.value === undefined ? '' : item.value;
return execInter; var src = '';
}; if (item.begin !== undefined && item.end != undefined)
src = source.slice(item.begin, item.end).replace('\n', '\\n', 'g');
</script> if (src.length > 30)
src = src.slice(0, 30) + '...';
if (item.name != 'tag')
text += ' ';
text += prefix + item.name + ' ' + v + '\t\t\t' + src + '\n';
});
text += prefix + '.data\n';
if (asm['.data'])
$.each(asm['.data'], function(i, item) {
text += ' ' + prefix + '' + i + ':\n';
text += formatAssemblyText(item, prefix + ' ', source);
});
return text;
};
$('.asmOutput button').click(function() {$(this).parent().find('pre').toggle(); } )
// ----------------- VM ----------------------
var stateTrie = new EthVm.Trie();
var vm = new EthVm.VM(stateTrie);
//@todo this does not calculate the gas costs correctly but gets the job done.
var identityCode = 'return { gasUsed: 1, return: opts.data, exception: 1 };';
var identityAddr = ethUtil.pad(new Buffer('04', 'hex'), 20)
vm.loadPrecompiled(identityAddr, identityCode);
var secretKey = '3cd7232cd6f3fc66a57a6bedc1a8ed6c228fff0a327e169c2bcc5e869ed49511'
var publicKey = '0406cc661590d48ee972944b35ad13ff03c7876eae3fd191e8a2f77311b0a3c6613407b5005e63d7d8d76b89d5f900cde691497688bb281e07a5052ff61edebdc0'
var address = ethUtil.pubToAddress(new Buffer(publicKey, 'hex'));
$('#txorigin').text('0x' + address.toString('hex'));
var account = new EthVm.Account();
account.balance = 'f00000000000000001';
var nonce = 0;
stateTrie.put(address, account.serialize());
var runTx = function(data, to, cb) {
var tx = new EthVm.Transaction({
nonce: new Buffer([nonce++]), //@todo count beyond 255
gasPrice: '01',
gasLimit: '3000000000', // plenty
to: to,
data: data
});
tx.sign(new Buffer(secretKey, 'hex'));
vm.runTx({tx: tx}, cb);
};
var getConstructorInterface = function(abi) {
var funABI = {'name':'','inputs':[],'type':'constructor','outputs':[]};
for (var i = 0; i < abi.length; i++)
if (abi[i].type == 'constructor') {
funABI.inputs = abi[i].inputs || [];
break;
}
return funABI;
};
var getCallButton = function(args) {
// args.abi, args.bytecode [constr only], args.address [fun only]
// args.appendFunctions [constr only]
var isConstructor = args.bytecode !== undefined;
var fun = new web3.eth.function(args.abi);
var inputs = '';
$.each(args.abi.inputs, function(i, inp) {
if (inputs != '') inputs += ', ';
inputs += inp.type + ' ' + inp.name;
});
var inputField = $('<input/>').attr('placeholder', inputs);
var outputSpan = $('<div class="output"/>');
var button = $('<button/>')
.text(args.bytecode ? 'Create' : fun.displayName())
.click(function() {
var funArgs = $.parseJSON('[' + inputField.val() + ']');
var data = fun.toPayload(funArgs).data;
if (data.slice(0, 2) == '0x') data = data.slice(2);
if (isConstructor)
data = args.bytecode + data.slice(8);
outputSpan.text('...');
runTx(data, args.address, function(err, result) {
if (err)
outputSpan.text(err);
else if (isConstructor) {
outputSpan.text(' Creation used ' + result.vm.gasUsed.toString(10) + ' gas.');
args.appendFunctions(result.createdAddress);
} else {
var outputObj = fun.unpackOutput('0x' + result.vm.return.toString('hex'));
outputSpan.text(' Returned: ' + JSON.stringify(outputObj));
}
});
});
if (!isConstructor)
button.addClass('runButton');
var c = $('<div class="contractProperty"/>')
.append(button);
if (args.abi.inputs.length > 0)
c.append(inputField);
return c.append(outputSpan);
};
var getExecuteInterface = function(contract, name) {
var abi = $.parseJSON(contract.interface);
var execInter = $('<div/>');
var funABI = getConstructorInterface(abi);
var appendFunctions = function(address) {
var instance = $('<div class="contractInstance"/>');
var title = $('<span class="title"/>').text('Contract at ' + address.toString('hex') );
instance.append(title);
$.each(abi, function(i, funABI) {
if (funABI.type != 'function') return;
instance.append(getCallButton({
abi: funABI,
address: address
}));
});
execInter.append(instance);
title.click(function(ev){ $(this).parent().toggleClass('hide') });
};
if (contract.bytecode.length > 0)
execInter
.append(getCallButton({
abi: funABI,
bytecode: contract.bytecode,
appendFunctions: appendFunctions
}));
return execInter;
};
</script>
</body> </body>
</html> </html>
...@@ -10,12 +10,66 @@ body { ...@@ -10,12 +10,66 @@ body {
width: auto; width: auto;
bottom: 0px; bottom: 0px;
right: 37em; right: 37em;
} }
#input {
#files {
font-size: 15px; font-size: 15px;
height: 2.5em;
box-sizing: border-box;
line-height: 2em;
padding: 0.5em 0.5em 0;
}
#files .file,
#files .newFile {
display: inline-block;
padding: 0 0.6em;
box-sizing: border-box;
background-color: #f0f0f0;
cursor: pointer;
margin-right: 0.5em;
position: relative;
}
#files .newFile {
background-color: #B1EAC5;
font-weight: bold;
color: #4E775D;
}
#files .file.active {
font-weight: bold;
border-bottom: 0 none;
padding-right: 2.5em;
}
#files .file .remove {
position: absolute; position: absolute;
right: 0;
top: 0; top: 0;
height: 1.25em;
width: 1.25em;
line-height: 1em;
border-radius: 1em;
color: #FF8080;
display: none;
margin: 0.4em;
text-align: center;
}
#files .file input {
background-color: transparent;
border: 0 none;
border-bottom: 1px dotted black;
line-height: 1em;
margin: 0.5em 0;
}
#files .file.active .remove { display: inline-block; }
#input {
font-size: 15px;
position: absolute;
top: 2.5em;
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
......
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